package com.turing.post.transaction.impl;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.ejb.EJB;
import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateless;

import com.turing.post.account.util.AccountBusiness;
import com.turing.post.account.util.AccountParam;
import com.turing.post.account.util.AccountSystemAddTradDetail;
import com.turing.post.account.util.AccountSystemAddTradDetailDao;
import com.turing.post.account.util.MasterAccount;
import com.turing.post.account.util.MasterAccountDao;
import com.turing.post.account.util.SubAccount;
import com.turing.post.account.util.SubAccountDao;
import com.turing.post.cardmanagement.util.ActivateCard;
import com.turing.post.cardmanagement.util.ActivateCardDao;
import com.turing.post.cardmanagement.util.MakeCardControl;
import com.turing.post.cardmanagement.util.MakeCardControlDao;
import com.turing.post.cardtransactions.util.CardTranApplicationException;
import com.turing.post.cardtransactions.util.CardTransBusiness;
import com.turing.post.cardtransactions.util.MrchDepositObject;
import com.turing.post.cardtransactions.util.PaidParam;
import com.turing.post.cardtransactions.util.PaidResult;
import com.turing.post.common.util.PCPSCommonDao;
import com.turing.post.common.util.SysOpUser;
import com.turing.post.common.util.SysOpUserDao;
import com.turing.post.organization.util.IssueCompany;
import com.turing.post.organization.util.IssueCompanyDao;
import com.turing.post.organization.util.Merchant;
import com.turing.post.organization.util.MerchantBranch;
import com.turing.post.organization.util.MerchantBranchDao;
import com.turing.post.organization.util.MerchantBusinessType;
import com.turing.post.organization.util.MerchantBusinessTypeDao;
import com.turing.post.organization.util.MerchantCardFee;
import com.turing.post.organization.util.MerchantDao;
import com.turing.post.organization.util.MerchantFeePolicy;
import com.turing.post.organization.util.MerchantFeePolicyBusiness;
import com.turing.post.organization.util.MerchantFeeSettlementPolicyDao;
import com.turing.post.organization.util.MerchantSecretPolicyBusiness;
import com.turing.post.report.util.BinaryFileInfo;
import com.turing.post.report.util.BinaryFileInfoDao;
import com.turing.post.transaction.util.ChkAccFile;
import com.turing.post.transaction.util.ChkAccFileBusiness;
import com.turing.post.transaction.util.MchAccDetail;
import com.turing.post.transaction.util.MchAccDetailDao;
import com.turing.post.transaction.util.MchAddCardDetail;
import com.turing.post.transaction.util.MchAddCardDetailDao;
import com.turing.post.transaction.util.MchAddTradDetail;
import com.turing.post.transaction.util.MchAddTradDetailDao;
import com.turing.post.transaction.util.MchAddTradSummary;
import com.turing.post.transaction.util.MchAddTradSummaryDao;
import com.turing.post.transaction.util.MchBsTypeTradDetail;
import com.turing.post.transaction.util.MchBsTypeTradDetailDao;
import com.turing.post.transaction.util.MchChkAccCtl;
import com.turing.post.transaction.util.MchChkAccCtlDao;
import com.turing.post.transaction.util.MchChkAccDetailDao;
import com.turing.post.transaction.util.MchMistakeObject;
import com.turing.post.transaction.util.MchTradDetail;
import com.turing.post.transaction.util.MchTradDetailDao;
import com.turing.post.transaction.util.MrchBusiness;
import com.turing.post.transaction.util.MrchTransParam;
import com.turing.post.transaction.util.TranApplicationException;
import com.turing.post.tuxedo.util.TuxedoRespCode;
import com.turing.post.util.PCPSApplicationException;
import com.turing.post.util.PCPSUtil;

@Stateless(mappedName = "transaction.impl.MrchBusinessImpl")
@Remote
@Local
public class MrchBusinessImpl implements MrchBusiness {

	@EJB(mappedName = "common.impl.PCPSCommonDaoImpl")
	private PCPSCommonDao pcpsCommonDaoImpl;

	@EJB(mappedName = "cardmanagement.impl.MakeCardControlDaoImpl")
	private MakeCardControlDao makeCardControlDaoImpl;

	@EJB(mappedName = "cardmanagement.impl.ActivateCardDaoImpl")
	private ActivateCardDao activateCardDaoImpl;

	@EJB(mappedName = "organization.impl.MerchantDaoImpl")
	private MerchantDao merchantDaoImpl;

	@EJB(mappedName = "organization.impl.MerchantBranchDaoImpl")
	private MerchantBranchDao merchantBranchDaoImpl;

	@EJB(mappedName = "transaction.impl.MchTradDetailDaoImpl")
	private MchTradDetailDao mchTradDetailDaoImpl;

	@EJB(mappedName = "transaction.impl.MchAddTradDetailDaoImpl")
	private MchAddTradDetailDao mchAddTradDetailDaoImpl;

	@EJB(mappedName = "transaction.impl.MchAddTradSummaryDaoImpl")
	private MchAddTradSummaryDao mchAddTradSummaryDaoImpl;

	@EJB(mappedName = "transaction.impl.MchAddCardDetailDaoImpl")
	private MchAddCardDetailDao mchAddCardDetailDaoImpl;

	@EJB(mappedName = "cardtransactions.impl.CardTransBusinessImpl")
	private CardTransBusiness cardTransBusinessImpl;

	@EJB(mappedName = "transaction.impl.MchAccDetailDaoImpl")
	private MchAccDetailDao mchAccDetailDaoImpl;

	@EJB(mappedName = "organization.impl.IssueCompanyDaoImpl")
	private IssueCompanyDao issueCompanyDaoImpl;

	// @EJB(mappedName = "organization.impl.SaleBranchDaoImpl")
	// private SaleBranchDao saleBranchDaoImpl;

	@EJB(mappedName = "account.impl.AccountSystemAddTradDetailDaoImpl")
	private AccountSystemAddTradDetailDao accountSystemAddTradDetailDaoImpl;

	@EJB(mappedName = "transaction.impl.ChkAccFileBusinessImpl")
	private ChkAccFileBusiness chkAccFileBusinessImpl;

	@EJB(mappedName = "transaction.impl.MchChkAccCtlDaoImpl")
	private MchChkAccCtlDao mchChkAccCtlDaoImpl;

	@EJB(mappedName = "transaction.impl.MchChkAccDetailDaoImpl")
	private MchChkAccDetailDao mchChkAccDetailDaoImpl;

	@EJB(mappedName = "report.impl.BinaryFileInfoDaoImpl")
	private BinaryFileInfoDao binaryFileInfoDaoImpl;

	@EJB(mappedName = "organization.impl.MerchantBusinessTypeDaoImpl")
	private MerchantBusinessTypeDao merchantBusinessTypeDaoImpl;

	@EJB(mappedName = "organization.impl.MerchantFeePolicyBusinessImpl")
	private MerchantFeePolicyBusiness merchantFeePolicyBusinessImpl;

	@EJB(mappedName = "organization.impl.MerchantSecretPolicyBusinessImpl")
	private MerchantSecretPolicyBusiness merchantSecretPolicyBusinessImpl;

	@EJB(mappedName = "organization.impl.MerchantFeeSettlementPolicyDaoImpl")
	private MerchantFeeSettlementPolicyDao merchantFeeSettlementPolicyDaoImpl;

	@EJB(mappedName = "transaction.impl.MchBsTypeTradDetailDaoImpl")
	private MchBsTypeTradDetailDao mchBsTypeTradDetailDaoImpl;

	@EJB(mappedName = "common.impl.SysOpUserDaoImpl")
	private SysOpUserDao sysOpUserDaoImpl;

	@EJB(mappedName = "account.impl.MasterAccountDaoImpl")
	private MasterAccountDao masterAccountDaoImpl;

	@EJB(mappedName = "account.impl.SubAccountDaoImpl")
	private SubAccountDao subAccountDaoImpl;

	@EJB(mappedName = "account.impl.AccountBusinessImpl")
	private AccountBusiness accountBusinessImpl;

	/**
	 * 检查交易时间,如为空则置为系统时间
	 * 
	 * @param cpc
	 */
	private void validateTranTime(MrchTransParam mtp) {
		// 取数据库时间
		if (mtp.curSysDateTime == null)
			mtp.curSysDateTime = pcpsCommonDaoImpl.getDatabaseSysTime();
	}

	private boolean compareString(String dbcontent, String cmpstr) {
		if (PCPSUtil.isEmpty(cmpstr))
			return true;

		return PCPSUtil.compareObject(dbcontent, cmpstr);
	}

	/**
	 * 获取有效的卡号
	 * 
	 * @param cardNo
	 * @param track2
	 * @param track3
	 * @return
	 * @throws PCPSApplicationException
	 */
	public String getValidateCardNo(String merchantNo, String cardNo,
			String track2, String track3) throws PCPSApplicationException {
		// 三个参数不能全部为空
		if (PCPSUtil.isEmpty(cardNo) && PCPSUtil.isEmpty(track2)
				&& PCPSUtil.isEmpty(track3))
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID,
					"卡号无效!");

		if (PCPSUtil.isEmpty(track2) && !PCPSUtil.isEmpty(track3))
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID,
					"二磁道无内容而三磁道有内容!");

		Merchant merchant = merchantDaoImpl.getMerchantByMerchantNo(merchantNo);
		if (merchant == null)
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					"商户号无效!");

		if (PCPSUtil.compareObject(merchant.getChannelMode(),
				Merchant.MERCHANT_CHANNELMODE_POS)) {
			if (PCPSUtil.isEmpty(track2))
				throw new TranApplicationException(TuxedoRespCode.RC_INVALID,
						"POS商户必须上送磁道信息!");
		}

		ActivateCard ac = null;
		if (!PCPSUtil.isEmpty(cardNo))
			ac = activateCardDaoImpl.getActivateCardByCardNo(cardNo);
		else if (!PCPSUtil.isEmpty(track2))
			ac = activateCardDaoImpl.getActivateCardByTrack23(track2, track3);

		if (ac == null || !compareString(ac.getCardNo(), cardNo)
				|| !compareString(ac.getTwoTrackContent(), track2)
				|| !compareString(ac.getThirdTrackContent(), track3))
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID,
					"卡号与二、三磁道内容不符!");

		return ac.getCardNo();
	}

	/**
	 * 支付业务入口参数检查 -- 公共部分
	 * 
	 * @param mtp
	 *            商户支付基础信息
	 */
	private void checkPaidBusinessArgs(MrchTransParam mtp)
			throws PCPSApplicationException {

		// 确保卡支付合计金额项不为null
		if (mtp.totalAmount == null)
			mtp.totalAmount = BigInteger.ZERO;

		// 确保现金不为 null
		if (mtp.otherPaidAmount == null)
			mtp.otherPaidAmount = BigInteger.ZERO;

		// 确保积分支付额不为null
		if (mtp.scorePaidAmount == null)
			mtp.scorePaidAmount = BigInteger.ZERO;

		// 商户检查
		if (PCPSUtil.isEmpty(mtp.merchantNo))
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					"商户号为空!");

		// 商户分支机构检查
		if (PCPSUtil.isEmpty(mtp.mrchBrchNo))
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					"商户分支机构号为空!");

		// 确保分支机构号存在
		MerchantBranch mb = merchantBranchDaoImpl
				.getMerchantBranchByMerchantTrans(mtp.merchantNo,
						mtp.mrchBrchNo);
		if (mb == null)
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					"商户分支机构号" + mtp.mrchBrchNo + "不存在!");
		// 重新进行映射
		mtp.mrchBrchNo = mb.getMerchantBranchNo();

		// 帐务日期检查
		if (mtp.tranDate == null)
			throw new TranApplicationException(TuxedoRespCode.RC_TRANDATE,
					"帐务日期为空!");
	}

	/**
	 * 支付业务入口参数检查
	 * 
	 * @param mtp
	 *            商户支付基础信息
	 */
	private void checkPaidArgs(MrchTransParam mtp)
			throws PCPSApplicationException {

		// 检查业务参数
		checkPaidBusinessArgs(mtp);

		// 请求流水号检查
		if (PCPSUtil.isEmpty(mtp.mrchReqSeqNo))
			throw new TranApplicationException(TuxedoRespCode.RC_REQSEQNO,
					"请求流水号为空!");

		// 发生额检查
		if (BigInteger.ZERO.compareTo(mtp.totalAmount) > 0
				|| BigInteger.ZERO.compareTo(mtp.otherPaidAmount) > 0
				|| BigInteger.ZERO.compareTo(mtp.scorePaidAmount) > 0)
			throw new TranApplicationException(TuxedoRespCode.RC_AMOUNT,
					"金额无效 !");

		// 合计额检查
		BigInteger totalpaid = mtp.totalAmount.add(mtp.scorePaidAmount);
		if (BigInteger.ZERO.compareTo(totalpaid) >= 0)
			throw new TranApplicationException(TuxedoRespCode.RC_AMOUNT,
					"合计发生额不符!");

		// 会员支付检查,积分支付>0但未提供会员卡或密码
		if (BigInteger.ZERO.compareTo(mtp.scorePaidAmount) < 0
				&& (PCPSUtil.isEmpty(mtp.membershipCardNo) || PCPSUtil
						.isEmpty(mtp.membershipCardPwd)))
			throw new PCPSApplicationException(TuxedoRespCode.RC_AMOUNT,
					"会员卡支付数据有误!");

		// 核对卡支付
		if (BigInteger.ZERO.compareTo(mtp.totalAmount) > 0) {
			if (mtp.cardAccMap == null || mtp.cardAccMap.size() < 1)
				throw new TranApplicationException(TuxedoRespCode.RC_AMOUNT,
						"预付卡金额有误!");
			BigInteger amt = BigInteger.ZERO;
			for (Map.Entry<String, MrchTransParam.CardAccount> entry : mtp.cardAccMap
					.entrySet()) {
				if (PCPSUtil.isEmpty(entry.getKey()))
					throw new CardTranApplicationException(
							TuxedoRespCode.RC_INVALID, "卡号无效!");
				MrchTransParam.CardAccount ca = entry.getValue();
				if (PCPSUtil.isEmpty(ca.cardPwd))
					throw new CardTranApplicationException(
							TuxedoRespCode.RC_PWDERR, "未提供密码!");
				if (ca.amount == null
						|| BigInteger.ZERO.compareTo(ca.amount) >= 0)
					throw new CardTranApplicationException(
							TuxedoRespCode.RC_AMOUNT, "金额非法!");

				// 检查2 & 3磁道 -- 目前暂未实现
				amt = amt.add(ca.amount);
			}

			if (!mtp.totalAmount.equals(amt))
				throw new TranApplicationException(TuxedoRespCode.RC_AMOUNT,
						"合计金额有误!");
		}

		// 如果商户消费时无需业务类型,则不检查
		Merchant merchant = merchantDaoImpl
				.getMerchantByMerchantNo(mtp.merchantNo);
		if (!PCPSUtil.compareObject(merchant.getNeedBusinessType(),
				Merchant.BUSINESSTYPE_NEED)) {
			mtp.matdlist = new ArrayList<MchAddTradDetail>();
			return;
		}

		// 检查商户消费交易的业务类型,商户如定义了业务类型,则验证,否则忽略
		List<MerchantBusinessType> mbtlist = merchantBusinessTypeDaoImpl
				.getAllMerchantBusinessTypeByMerchantNo(mtp.merchantNo);
		if (mbtlist == null || mbtlist.size() < 1)
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
					"发卡商商户业务/服务/商品类型未设置!");

		if (mtp.matdlist == null || mtp.matdlist.size() < 1)
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
					"请求报文中商户业务/服务/商品类型未设置!");

		Map<String, String> mbtype = new HashMap<String, String>();
		BigInteger allamt = BigInteger.ZERO;

		// 循环检查业务类型的存在性
		for (MchAddTradDetail matd : mtp.matdlist) {
			String prodid = matd.getMrchProdId();
			if (PCPSUtil.isEmpty(prodid))
				throw new TranApplicationException(
						TuxedoRespCode.RC_INVALID_TRANS, "商户业务/服务/商品类型为空!");

			if (mbtype.containsKey(prodid))
				throw new TranApplicationException(
						TuxedoRespCode.RC_INVALID_TRANS, "重复的商户业务/服务/商品类型["
								+ prodid + "]!");

			MerchantBusinessType mbt = new MerchantBusinessType();
			mbt.setMerchantNo(mtp.merchantNo);
			mbt.setBusinessType(prodid);
			mbt = merchantBusinessTypeDaoImpl.getMerchantBusinessTypeByKey(mbt);
			if (mbt == null)
				throw new TranApplicationException(
						TuxedoRespCode.RC_INVALID_TRANS, "无效的商户业务/服务/商品类型["
								+ prodid + "]!");

			mbtype.put(prodid, prodid);
			BigInteger amt = matd.getMrchProdAmount();
			if (amt == null || BigInteger.ZERO.compareTo(amt) >= 0)
				throw new TranApplicationException(
						TuxedoRespCode.RC_INVALID_TRANS, "商户业务/服务/商品类型["
								+ prodid + "]对应的发生额非法F!");

			allamt = allamt.add(amt);
		}

		// 检查金额的一致性
		BigInteger orderamt = mtp.totalAmount.add(mtp.scorePaidAmount).add(
				mtp.otherPaidAmount);
		if (!orderamt.equals(allamt))
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
					String.format(
							"预付卡合计[%s] + 积分支付[%s] + 其他方式支付[%s] <> 业务合计[%s]!",
							mtp.totalAmount.toString(), mtp.scorePaidAmount
									.toString(),
							mtp.otherPaidAmount.toString(), allamt.toString()));
	}

	/**
	 * 商户交易一般性检查
	 * 
	 * @param merchantNo
	 * @param tranDate
	 * @return
	 * @throws PCPSApplicationException
	 */
	private Merchant chkMerchantCommon(String merchantNo, Date tranDate)
			throws PCPSApplicationException {
		// 检索商户信息
		Merchant merchant = merchantDaoImpl.getMerchantByMerchantNo(merchantNo);
		if (merchant == null)
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					"商户号" + merchantNo + "不存在!");

		// 判断是否激活
		if (!merchant.getActivate().equals(Merchant.MERCHANT_ACTIVATE))
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					"商户尚未激活!");

		int result = tranDate.compareTo(merchant.getTranDate());

		PCPSUtil.Log(tranDate.toString());

		// 当前的日期不能<Merchant.TranDate
		if (result < 0)
			throw new TranApplicationException(TuxedoRespCode.RC_DISTRAN,
					"日期不能比商户当前的帐务日期早!");

		// 与当前交易日期一致,继续
		if (result > 0) {
			PCPSUtil.Log(String.format("switch merchant(%s) trandate to (%s)!",
					merchant.getMerchantNo(), PCPSUtil
							.getPackedYMDString(tranDate)));
			merchant.setTranDate(tranDate);
			merchantDaoImpl.updateMerchant(merchant);
		}

		// 判断是否定义了商户的结算策略 -- 2013.07.17
		int policy = merchantFeeSettlementPolicyDaoImpl
				.getSettlementPolicyCategory(merchantNo, tranDate);
		if (policy == 0)
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					String.format("尚未定义商户[%s][%s][%s]的结算策略!", merchantNo,
							merchant.getMerchantName(), PCPSUtil
									.getPackedYMDString(tranDate)));

		int pret = PCPSUtil.compareObject(merchant.getNeedBusinessType(),
				Merchant.BUSINESSTYPE_NEED) ? 0x80 : 0x01;

		// 检查商户结算策略是否一致 -- 2013.07.17
		if (policy != pret)
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					String.format("商户[%s][%s][%s]的结算策略与商户是否需要业务类型冲突!",
							merchantNo, merchant.getMerchantName(), PCPSUtil
									.getPackedYMDString(tranDate)));

		return (merchant);
	}

	/**
	 * 恢复原始交易的状态
	 * 
	 * @param origmtd
	 * @throws PCPSApplicationException
	 */
	private void restoreOrigMchTradDetail(MchTradDetail origmtd)
			throws PCPSApplicationException {

		// 查找被撤销的原始交易
		List<MchTradDetail> mtdlist = mchTradDetailDaoImpl
				.getMchTradDetailByMrchSeqNo(origmtd.getMerchantNo(), origmtd
						.getOtherTranDate(), origmtd.getOtherMrchSeqNo());
		if (mtdlist != null && mtdlist.size() == 1) {
			MchTradDetail mtd = mtdlist.get(0);

			// 恢复mtd的other字段为空,采用直接update方法
			int result = mchTradDetailDaoImpl.resetMchTradDetailOtherInf(mtd
					.getMerchantNo(), mtd.getTranDate(), mtd.getTranSeqNo());
			if (result == 1)
				return;
		}

		PCPSUtil.Log("merchantno:" + origmtd.getMerchantNo() + " trandate:"
				+ origmtd.getTranDate() + " transeqno:"
				+ origmtd.getTranSeqNo());
		throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
				"被撤销的原始交易未找到!");
	}

	/**
	 * 本地支付业务商户状态检查逻辑
	 * 
	 * @param mtp
	 *            商户支付基础信息
	 * @return Merchant
	 */
	private Merchant chkMerchantCommonForBusiness(MrchTransParam mtp)
			throws PCPSApplicationException {

		// 检索商户信息
		Merchant merchant = chkMerchantCommon(mtp.merchantNo, mtp.tranDate);

		// 如果传入的发卡商未设置
		if (PCPSUtil.isEmpty(mtp.issueCompanyNo))
			mtp.issueCompanyNo = merchant.getIssueCompanyNo();

		// int result = mtp.TranDate.compareTo(merchant.getTranDate());

		// // 与当前交易日期一致,继续
		// if (result == 0) {
		// // 日期一致的情况下，如果对帐模式是按商户对帐且TranStatus不为交易状态,则返回异常
		// if (merchant.getCheckAccMode().equals(
		// Merchant.CHECK_ACC_MODE_BY_MERCHANT)
		// && !merchant.getTranStatus().equals(
		// Merchant.MERCHANT_TRANSTATUS))
		// throw new TranApplicationException(TranRespCode.RC_DISTRAN,
		// "NEED MERCHANT[" + mtp.MerchantNo + "]DAYOFF FIRST");
		// return (merchant);
		// }
		//
		// // 日期不一致,检查对帐模式
		// if (merchant.getCheckAccMode().equals(
		// Merchant.CHECK_ACC_MODE_BY_MERCHANT))
		// // 必须对帐,且由商户主动发起
		// throw new TranApplicationException(TranRespCode.RC_TRANDATE,
		// "Merchant Need CheckAcc Trad Exec First!");
		// else if (merchant.getCheckAccMode().equals(
		// Merchant.CHECK_ACC_MODE_NEEDNT)) {
		// // 无需对帐, 超过当前日期,执行商户帐务日期自动切换
		// // merchant = autoDayoffMerchant(merchant.getMerchantNo(), mpc
		// // .getTranDate());
		// } else
		// // 对帐模式非法
		// throw new TranApplicationException(TranRespCode.RC_DISTRAN,
		// "Merchant CheckAccMode invalid!");

		// 返回当前商户
		return (merchant);
	}

	/**
	 * 检查商户传来的请求流水号是否唯一
	 * 
	 * @param mtp
	 *            商户支付基础信息
	 */
	private void checkMrchSeqNo(MrchTransParam mtp)
			throws PCPSApplicationException {
		String MrchReqSeqNo = mtp.mrchReqSeqNo;
		if (PCPSUtil.isEmpty(MrchReqSeqNo))
			throw new TranApplicationException(TuxedoRespCode.RC_REQSEQNO,
					"请求流水号为NULL!");
		List<MchTradDetail> mtd = mchTradDetailDaoImpl
				.getMchTradDetailByMrchSeqNo(mtp.merchantNo, mtp.tranDate,
						MrchReqSeqNo);
		if (mtd != null && mtd.size() > 0)
			throw new TranApplicationException(TuxedoRespCode.RC_REQSEQNO,
					"请求流水号" + MrchReqSeqNo + "重复!");
	}

	private String generateMerchantTranSeqNo(Date timestamp)
			throws PCPSApplicationException {
		// 产生流水顺序号
		BigInteger mrchseqno = pcpsCommonDaoImpl.getMrchTranSeqNo();
		int seq = mrchseqno.mod(BigInteger.valueOf(10000)).intValue();
		if (seq < 0)
			seq = 0;
		String strseq = "";
		if (seq < 10)
			strseq = "000" + seq;
		else if (seq < 100)
			strseq = "00" + seq;
		else if (seq < 1000)
			strseq = "0" + seq;
		else
			strseq = "" + seq;
		String TranSeqNo = new SimpleDateFormat("MMddHHmm").format(timestamp)
				+ strseq;
		if (TranSeqNo.length() != 12)
			throw new TranApplicationException(TuxedoRespCode.RC_DBERR,
					"Generate TranSeqNo error" + TranSeqNo + "!");
		return TranSeqNo;
	}

	/**
	 * 产生交易流水号
	 * 
	 * @param merchant
	 * @param mtp
	 * @return
	 * @throws PCPSApplicationException
	 */
	private String generateMerchantTranSeqNo(Merchant merchant,
			MrchTransParam mtp) throws PCPSApplicationException {
		// 产生流水顺序号
		String TranSeqNo = generateMerchantTranSeqNo(mtp.curSysDateTime);

		// 设置商户交易流水号 -- 客户端返回数据
		mtp.tranSeqNo = TranSeqNo;

		return (TranSeqNo);
	}

	/**
	 * 根据交易请求记载交易流水
	 * 
	 * @param mtd
	 */
	private MchTradDetail createMchTradDetail(MrchTransParam mtp)
			throws PCPSApplicationException {
		MchTradDetail mtd = new MchTradDetail();
		mtd.setMerchantNo(mtp.merchantNo);
		mtd.setTranDate(mtp.tranDate);
		mtd.setTranSeqNo(mtp.tranSeqNo);
		mtd.setCancelTranSeqNo(null);
		mtd.setTranTime(mtp.curSysDateTime);
		mtd.setMrchSeqNo(mtp.mrchReqSeqNo);

		if (mtp.cardAccMap != null && mtp.cardAccMap.size() > 0) {
			// 取第1个
			for (Map.Entry<String, MrchTransParam.CardAccount> entry : mtp.cardAccMap
					.entrySet()) {
				mtd.setCardNo(entry.getKey());
				break;
			}
		} else
			mtd.setCardNo(null);

		// 卡系统相关 -- 暂时置null
		mtd.setAccAckSeqNo(null);

		// 商户业务摘要
		mtd.setMrchAbstract(mtp.mrchAbstract);
		// 会员及积分信息
		mtd.setVipCardNo(mtp.membershipCardNo);

		// 其他辅助信息 -- 终端 &机构
		mtd.setTerminalNo(mtp.mrchPosNo);
		mtd.setTermSettBatchNo(mtp.mrchPosBatchNo);
		mtd.setTermSeqNo(mtp.mrchPosSeqNo);
		mtd.setMrchTellerNo(mtp.mrchTellerNo);
		mtd.setMrchBrchNo(mtp.mrchBrchNo);

		return (mtd);
	}

	/**
	 * 根据当前交易入口参数及历史交易明细,创建被撤销或冲正流水记录
	 * 
	 * @param mtp
	 * @param origmtd
	 * @return
	 * @throws PCPSApplicationException
	 */
	private MchTradDetail createMchTradDetailByOrig(MrchTransParam mtp,
			MchTradDetail origmtd) throws PCPSApplicationException {
		MchTradDetail mtd = new MchTradDetail();

		mtd.setMerchantNo(mtp.merchantNo);
		mtd.setTranDate(mtp.tranDate);
		mtd.setTranSeqNo(mtp.tranSeqNo);
		mtd.setCancelTranSeqNo(null);
		mtd.setTranTime(mtp.curSysDateTime);
		mtd.setMrchSeqNo(mtp.mrchReqSeqNo);

		// 卡号置为原交易卡号
		mtd.setCardNo(origmtd.getCardNo());

		// 卡系统相关 -- 暂时置null
		mtd.setAccAckSeqNo(null);

		// 商户业务摘要
		mtd.setMrchAbstract(mtp.mrchAbstract);

		// 会员及积分信息
		mtd.setVipCardNo(origmtd.getVipCardNo());

		// 其他辅助信息 -- 终端 &机构
		mtd.setTerminalNo(mtp.mrchPosNo);
		mtd.setTermSettBatchNo(mtp.mrchPosBatchNo);
		mtd.setTermSeqNo(mtp.mrchPosSeqNo);
		mtd.setMrchTellerNo(mtp.mrchTellerNo);
		mtd.setMrchBrchNo(mtp.mrchBrchNo);

		return (mtd);
	}

	/**
	 * 附加产品信息检查
	 * 
	 * @param matd
	 * @throws PCPSApplicationException
	 */
	private MerchantBusinessType checkProdInf(MchAddTradDetail matd)
			throws PCPSApplicationException {
		if (PCPSUtil.isEmpty(matd.getMrchProdId())
				|| matd.getMrchProdQuantity() == null
				|| matd.getMrchProdQuantity().compareTo(BigInteger.ONE) < 0
				|| matd.getMrchProdPrice() == null
				|| matd.getMrchProdPrice().compareTo(BigInteger.ONE) < 0
				|| matd.getMrchProdAmount() == null
				|| matd.getMrchProdAmount().compareTo(BigInteger.ONE) < 0
				|| matd.getMrchProdCost() == null
				|| matd.getMrchProdCost().compareTo(BigInteger.ONE) < 0
				|| matd.getMrchProdFactSale() == null
				|| matd.getMrchProdFactSale().compareTo(BigInteger.ONE) < 0)
			throw new TranApplicationException(TuxedoRespCode.RC_PARAMETER,
					"checkProdInf");

		// 检查业务类型的合法性
		MerchantBusinessType mbt = new MerchantBusinessType();
		mbt.setMerchantNo(matd.getMerchantNo());
		mbt.setBusinessType(matd.getMrchProdId());
		mbt = merchantBusinessTypeDaoImpl.getMerchantBusinessTypeByKey(mbt);
		if (mbt == null)
			throw new TranApplicationException(TuxedoRespCode.RC_PARAMETER,
					"业务类型[" + matd.getMrchProdId() + "]非法!");

		return mbt;
	}

	/**
	 * 创建支付附加产品信息
	 * 
	 * @param mtp
	 * @throws PCPSApplicationException
	 */
	private void createMchAddTradDetail(MrchTransParam mtp)
			throws PCPSApplicationException {
		PCPSUtil.Log("createMchAddTradDetail...");

		// 产品明细笔数
		List<MchAddTradDetail> matd = mtp.matdlist;
		if (matd == null || matd.size() < 1)
			return;

		MchAddTradDetail cd;

		BigInteger total = BigInteger.ZERO;

		Map<String, String> allprod = new HashMap<String, String>();

		for (int i = 0; i < matd.size(); i++) {
			cd = matd.get(i);
			// 主键信息
			cd.setMerchantNo(mtp.merchantNo);
			cd.setTranDate(mtp.tranDate);
			cd.setTranSeqNo(mtp.tranSeqNo);
			cd.setProdSeqNo(i);

			if (allprod.containsKey(cd.getMrchProdId()))
				throw new TranApplicationException(TuxedoRespCode.RC_PARAMETER,
						"业务类型" + cd.getMrchProdId() + "重复!");

			// 产品信息检查
			MerchantBusinessType mbt = checkProdInf(cd);
			cd.setMrchProdName(mbt.getBusinessName());

			allprod.put(cd.getMrchProdId(), cd.getMrchProdId());

			BigInteger bdamt = cd.getMrchProdQuantity().multiply(
					cd.getMrchProdFactSale());
			if (bdamt.compareTo(BigInteger.ONE) < 0)
				throw new TranApplicationException(TuxedoRespCode.RC_PARAMETER);

			total = total.add(bdamt);

			// 插入到数据库
			mchAddTradDetailDaoImpl.insert(cd);
		}

		// 核对产品总额
		BigInteger totalamt = mtp.totalAmount.add(mtp.otherPaidAmount).add(
				mtp.scorePaidAmount);
		if (totalamt.subtract(total).abs().compareTo(BigInteger.ONE) >= 0)
			throw new TranApplicationException(TuxedoRespCode.RC_PARAMETER,
					totalamt.toString() + "/" + total.toString());
	}

	/**
	 * 根据历史交易明细创建商户附加产品流水
	 * 
	 * @param mtp
	 * @param origmtd
	 * @throws PCPSApplicationException
	 */
	private void createMchAddTradDetailByOrig(MrchTransParam mtp,
			MchTradDetail origmtd) throws PCPSApplicationException {
		List<MchAddTradDetail> matdlist = mchAddTradDetailDaoImpl
				.getAllMchAddTradDetailByTranSeqNo(origmtd.getMerchantNo(),
						origmtd.getTranDate(), origmtd.getTranSeqNo());
		for (MchAddTradDetail matd : matdlist) {
			MchAddTradDetail nmatd = new MchAddTradDetail();

			nmatd.setMerchantNo(matd.getMerchantNo());

			// 日期和流水号取当前
			nmatd.setTranDate(mtp.tranDate);
			nmatd.setTranSeqNo(mtp.tranSeqNo);

			nmatd.setProdSeqNo(matd.getProdSeqNo());
			nmatd.setMrchProdId(matd.getMrchProdId());
			nmatd.setMrchProdName(matd.getMrchProdName());
			nmatd.setMrchProdQuantity(matd.getMrchProdQuantity());

			// 金额变相反数
			nmatd.setMrchProdPrice(BigInteger.ZERO.subtract(matd
					.getMrchProdPrice()));
			nmatd.setMrchProdAmount(BigInteger.ZERO.subtract(matd
					.getMrchProdAmount()));
			nmatd.setMrchProdCost(BigInteger.ZERO.subtract(matd
					.getMrchProdCost()));
			nmatd.setMrchProdFactSale(BigInteger.ZERO.subtract(matd
					.getMrchProdFactSale()));
			nmatd.setMrchProdProfitSYS(BigInteger.ZERO.subtract(matd
					.getMrchProdProfitSYS()));
			nmatd.setMrchProdProfitMCH(BigInteger.ZERO.subtract(matd
					.getMrchProdProfitMCH()));

			/* 插入新流水 */
			mchAddTradDetailDaoImpl.insert(nmatd);
		}
	}

	/**
	 * 创建支付附加信息
	 * 
	 * @param mtp
	 * @throws PCPSApplicationException
	 */
	private void createMchAddTradSummary(MrchTransParam mtp)
			throws PCPSApplicationException {
		try {
			PCPSUtil.Log("createMchAddTradSummary...");

			MchAddTradSummary cd = new MchAddTradSummary();

			// 主键信息
			cd.setMerchantNo(mtp.merchantNo);
			cd.setTranDate(mtp.tranDate);
			cd.setTranSeqNo(mtp.tranSeqNo);

			// 附加信息
			cd.setMrchBrchNo(mtp.mrchBrchNo);
			cd.setMrchBrchName(mtp.mrchBrchName);
			cd.setMrchTellerNo(mtp.mrchTellerNo);
			cd.setMrchTellerName(mtp.mrchTellerName);

			// 其他发生额
			BigInteger otherpaid = mtp.otherPaidAmount;
			if (otherpaid == null || otherpaid.longValue() == 0)
				cd.setMrchOtherPaidAmount(BigInteger.ZERO);
			else
				cd.setMrchOtherPaidAmount(otherpaid);

			// 卡发生额合计
			BigInteger amt = mtp.totalAmount;
			if (amt == null || amt.longValue() == 0)
				cd.setMrchAmount(BigInteger.ZERO);
			else
				cd.setMrchAmount(amt);

			// 产品明细笔数
			if (mtp.matdlist == null)
				cd.setMrchProdDetailNum(0);
			else
				cd.setMrchProdDetailNum(mtp.matdlist.size());

			mchAddTradSummaryDaoImpl.insert(cd);
		} catch (Exception ex) {
			PCPSUtil.Log(ex.toString());
			throw new TranApplicationException(
					TranApplicationException.ERRCODE_CATEGORY_DATABASE,
					TuxedoRespCode.RC_DBERR, ex.toString());
		}
	}

	/**
	 * 根据历史交易明细创建附加业务类型流水
	 * 
	 * @param mtp
	 * @param origmtd
	 * @throws PCPSApplicationException
	 */
	private void createMchBsTypeTradDetailByOrig(MrchTransParam mtp,
			MchTradDetail origmtd) throws PCPSApplicationException {
		List<MchBsTypeTradDetail> mbtdlist = mchBsTypeTradDetailDaoImpl
				.getAllMchBsTypeTradDetailByTran(origmtd.getMerchantNo(),
						origmtd.getTranDate(), origmtd.getTranSeqNo());
		for (MchBsTypeTradDetail mbtd : mbtdlist) {
			MchBsTypeTradDetail nmbtd = new MchBsTypeTradDetail();

			nmbtd.setMerchantNo(mbtd.getMerchantNo());

			// 日期及流水号取当前
			nmbtd.setTranDate(mtp.tranDate);
			nmbtd.setTranSeqNo(mtp.tranSeqNo);

			nmbtd.setBusinessType(mbtd.getBusinessType());

			// 金额取相反数
			nmbtd.setCardAmount(BigInteger.ZERO.subtract(mbtd.getCardAmount()));
			nmbtd.setScoreAmount(BigInteger.ZERO
					.subtract(mbtd.getScoreAmount()));
			nmbtd.setOtherAmount(BigInteger.ZERO
					.subtract(mbtd.getOtherAmount()));

			mchBsTypeTradDetailDaoImpl.insert(nmbtd);
		}
	}

	/**
	 * 根据历史交易明细创建附加流水
	 * 
	 * @param mtp
	 * @param origmtd
	 * @throws PCPSApplicationException
	 */
	private void createMchAddTradSummaryByOrig(MrchTransParam mtp,
			MchTradDetail origmtd) throws PCPSApplicationException {
		// 根据历史交易明细创建附加业务类型流水
		createMchBsTypeTradDetailByOrig(mtp, origmtd);

		List<MchAddTradSummary> matslist = mchAddTradSummaryDaoImpl
				.getAllMchAddTradSummaryByTranSeqNo(origmtd.getMerchantNo(),
						origmtd.getTranDate(), origmtd.getTranSeqNo());
		for (MchAddTradSummary mats : matslist) {
			MchAddTradSummary nmats = new MchAddTradSummary();

			nmats.setMerchantNo(mats.getMerchantNo());

			// 日期和流水号取当前
			nmats.setTranDate(mtp.tranDate);
			nmats.setTranSeqNo(mtp.tranSeqNo);

			nmats.setMrchBrchNo(mats.getMrchBrchNo());
			nmats.setMrchBrchName(mats.getMrchBrchName());
			nmats.setMrchTellerNo(mats.getMrchTellerNo());
			nmats.setMrchTellerName(mats.getMrchTellerName());
			nmats.setMrchProdDetailNum(mats.getMrchProdDetailNum());

			// 金额变相反数
			nmats.setMrchAmount(BigInteger.ZERO.subtract(mats.getMrchAmount()));
			nmats.setMrchOtherPaidAmount(BigInteger.ZERO.subtract(mats
					.getMrchOtherPaidAmount()));

			// 创建新流水
			mchAddTradSummaryDaoImpl.insert(nmats);
		}
	}

	/**
	 * 创建用户卡详细信息
	 * 
	 * @param mtp
	 * @throws PCPSApplicationException
	 */
	private void createMchAddCardDetail(MrchTransParam mtp)
			throws PCPSApplicationException {
		try {
			PCPSUtil.Log("createMchAddCardDetail...");

			// 如果不存在卡信息,则无需记载用户卡详细信息
			if (mtp.cardAccMap == null || mtp.cardAccMap.size() < 1)
				return;

			BigInteger total = BigInteger.ZERO;

			for (Map.Entry<String, MrchTransParam.CardAccount> entry : mtp.cardAccMap
					.entrySet()) {

				MchAddCardDetail cd = new MchAddCardDetail();

				cd.setMerchantNo(mtp.merchantNo);
				cd.setTranDate(mtp.tranDate);
				cd.setTranSeqNo(mtp.tranSeqNo);

				MrchTransParam.CardAccount ca = entry.getValue();
				cd.setCardNo(ca.cardNo);
				cd.setAmount(ca.amount);

				if (PCPSUtil.isEmpty(cd.getCardNo()))
					throw new TranApplicationException(
							TuxedoRespCode.RC_INVALID, "卡号为空!");

				if (cd.getAmount() == null
						|| cd.getAmount().compareTo(BigInteger.ZERO) <= 0)
					throw new TranApplicationException(
							TuxedoRespCode.RC_AMOUNT, "发生额非法");

				total = total.add(cd.getAmount());

				mchAddCardDetailDaoImpl.insert(cd);
			}

			if (!total.equals(mtp.totalAmount))
				throw new TranApplicationException(TuxedoRespCode.RC_AMOUNT,
						total.toString() + " <> " + mtp.totalAmount.toString());
		} catch (Exception ex) {
			PCPSUtil.Log(ex.toString());
			throw new TranApplicationException(
					TranApplicationException.ERRCODE_CATEGORY_DATABASE,
					TuxedoRespCode.RC_DBERR, ex.toString());
		}
	}

	/**
	 * 根据历史流水创建附加卡信息
	 * 
	 * @param mtp
	 * @param origmtd
	 * @throws PCPSApplicationException
	 */
	private void createMchAddCardDetailByOrig(MrchTransParam mtp,
			MchTradDetail origmtd) throws PCPSApplicationException {
		List<MchAddCardDetail> macdlist = mchAddCardDetailDaoImpl
				.getAllMchAddCardDetailByTranSeqNo(origmtd.getMerchantNo(),
						origmtd.getTranDate(), origmtd.getTranSeqNo());
		for (MchAddCardDetail macd : macdlist) {
			MchAddCardDetail nmacd = new MchAddCardDetail();

			nmacd.setMerchantNo(macd.getMerchantNo());

			// 日期和流水号取当前
			nmacd.setTranDate(mtp.tranDate);
			nmacd.setTranSeqNo(mtp.tranSeqNo);

			nmacd.setCardNo(macd.getCardNo());

			// 金额变相反数
			nmacd.setAmount(BigInteger.ZERO.subtract(macd.getAmount()));

			// 创建新流水
			mchAddCardDetailDaoImpl.insert(nmacd);
		}
	}

	/**
	 * 根据历史交易明细创建交易详细记帐信息
	 * 
	 * @param origmtd
	 * @param mtd
	 * @throws PCPSApplicationException
	 */
	private void createMchAccDetail(MchTradDetail origmtd, MchTradDetail mtd)
			throws PCPSApplicationException {
		List<MchAccDetail> madlist = mchAccDetailDaoImpl
				.getAllMchAccDetailByTranSeqNo(origmtd.getMerchantNo(), origmtd
						.getTranDate(), origmtd.getTranSeqNo());
		for (MchAccDetail mad : madlist) {
			MchAccDetail nmad = new MchAccDetail();

			// 记帐流水号取最新
			nmad.setAccSeqNo(mtd.getAccAckSeqNo());

			nmad.setAccSubSeqNo(mad.getAccSubSeqNo());
			nmad.setMerchantNo(mad.getMerchantNo());

			// 日期和流水号取当前
			nmad.setTranDate(mtd.getTranDate());
			nmad.setTranSeqNo(mtd.getTranSeqNo());

			nmad.setCardNo(mad.getCardNo());
			nmad.setMasterAccNo(mad.getMasterAccNo());
			nmad.setSubAccNo(mad.getSubAccNo());
			nmad.setOpenBranchNo(mad.getOpenBranchNo());
			nmad.setSalesGroupId(mad.getSalesGroupId());

			// 金额变相反数
			nmad.setPaidAmount(BigInteger.ZERO.subtract(mad.getPaidAmount()));
			nmad.setDepositAmount(BigInteger.ZERO.subtract(mad
					.getDepositAmount()));
			nmad.setFeeAmount(BigInteger.ZERO.subtract(mad.getFeeAmount()));

			// 创建新流水
			mchAccDetailDaoImpl.insert(nmad);
		}
	}

	/**
	 * 创建附加交易类型详细信息
	 * 
	 * @param mtp
	 * @throws PCPSApplicationException
	 */
	private void createMchBsTypeTradDetail(MrchTransParam mtp,
			Map<String, Map<String, BigInteger>> allbsmap)
			throws PCPSApplicationException {
		for (Map.Entry<String, Map<String, BigInteger>> entry : allbsmap
				.entrySet()) {
			String businessType = entry.getKey();

			// 2013.05.03 modify this line
			if (PCPSUtil.compareObject(businessType, NOBSTYPE_LABEL)) {
				// businessType = NOBSTYPE_LABEL;
				continue;
			}
			// 预付卡合计
			BigInteger tot = BigInteger.ZERO;
			// 积分合计
			BigInteger score = BigInteger.ZERO;
			// 其他合计
			BigInteger oth = BigInteger.ZERO;
			for (Map.Entry<String, BigInteger> bsamt : entry.getValue()
					.entrySet()) {
				String cardno = bsamt.getKey();
				BigInteger amt = bsamt.getValue();
				if (PCPSUtil.compareObject(cardno, SCORE_LABEL))
					score = score.add(amt);
				else if (PCPSUtil.compareObject(cardno, OTHER_LABEL))
					oth = oth.add(amt);
				else
					tot = tot.add(amt);
			}

			MchBsTypeTradDetail td = new MchBsTypeTradDetail();

			td.setMerchantNo(mtp.merchantNo);
			td.setTranDate(mtp.tranDate);
			td.setTranSeqNo(mtp.tranSeqNo);

			td.setBusinessType(businessType);
			td.setCardAmount(tot);
			td.setScoreAmount(score);
			td.setOtherAmount(oth);

			mchBsTypeTradDetailDaoImpl.insert(td);
		}
	}

	// 按商户检查消费类限制
	private void chkMerchantPaidLimit(MrchTransParam mtp, Merchant merchant)
			throws PCPSApplicationException {
		// 检查商户消费支付限制参数设置是否正确
		if (BigInteger.ZERO.compareTo(merchant.getDayPaidCountLimit()) > 0
				|| BigInteger.ZERO.compareTo(merchant.getDayPaidTotalLimit()) > 0
				|| BigInteger.ZERO.compareTo(merchant.getEachPaidLimit()) > 0)
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
					String.format("商户%s支付交易参数错!", merchant.getMerchantNo()));

		// 如果三个参数全无限制
		if (BigInteger.ZERO.compareTo(merchant.getDayPaidCountLimit()) == 0
				&& BigInteger.ZERO.compareTo(merchant.getDayPaidTotalLimit()) == 0
				&& BigInteger.ZERO.compareTo(merchant.getEachPaidLimit()) == 0)
			return;

		// 本笔交易总额
		BigInteger amt = mtp.totalAmount.add(mtp.scorePaidAmount);

		// 检查单笔限制
		if (BigInteger.ZERO.compareTo(merchant.getEachPaidLimit()) < 0) {
			if (merchant.getEachPaidLimit().compareTo(amt) < 0)
				throw new TranApplicationException(
						TuxedoRespCode.RC_OverOne_Paid, String.format(
								"超过了商户%s单笔支付消费限制%s!", merchant.getMerchantNo(),
								merchant.getEachPaidLimit().divide(
										BigInteger.valueOf(100)).toString()));
		}

		// 取商户当日消费次数及金额
		MchTradDetail mtd = mchTradDetailDaoImpl
				.getMchTradDetailDayPaidSummary(merchant.getMerchantNo(),
						mtp.tranDate);

		BigInteger dayPaidAmt = mtd.getAmount();
		BigInteger dayPaidCount = mtd.getFee();

		// 检查日消费次数
		if (BigInteger.ZERO.compareTo(merchant.getDayPaidCountLimit()) < 0) {
			if (merchant.getDayPaidCountLimit().compareTo(
					dayPaidCount.add(BigInteger.ONE)) < 0)
				throw new TranApplicationException(
						TuxedoRespCode.RC_DayNum_Paid, String.format(
								"超过了商户%s日支付消费限制次数%s!",
								merchant.getMerchantNo(), merchant
										.getDayPaidCountLimit().toString()));
		}

		// 检查日消费金额
		if (BigInteger.ZERO.compareTo(merchant.getDayPaidTotalLimit()) < 0) {
			if (merchant.getDayPaidTotalLimit().compareTo(dayPaidAmt.add(amt)) < 0)
				throw new TranApplicationException(
						TuxedoRespCode.RC_DayAmount_Paid, String.format(
								"超过了商户%s日支付消费限制金额%s!",
								merchant.getMerchantNo(), merchant
										.getDayPaidTotalLimit().divide(
												BigInteger.valueOf(100))
										.toString()));
		}
	}

	/**
	 * 本地支付业务逻辑
	 * 
	 * @param mtp
	 *            商户支付基础信息
	 * @return MerchantPaidCommon
	 */
	private MchTradDetail LocalPaid(MrchTransParam mtp)
			throws PCPSApplicationException {
		// 商户支付检查
		Merchant merchant = chkMerchantCommonForBusiness(mtp);

		// 商户请求流水号唯一性检查
		checkMrchSeqNo(mtp);

		// 如果要求商户在消费时附带业务类型但未上送,则异常
		if (PCPSUtil.compareObject(merchant.getNeedBusinessType(),
				Merchant.BUSINESSTYPE_NEED)
				&& (mtp.matdlist == null || mtp.matdlist.size() < 1))
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
					"需要上送附加的业务类型!");
		else if (!PCPSUtil.compareObject(merchant.getNeedBusinessType(),
				Merchant.BUSINESSTYPE_NEED)
				&& mtp.matdlist != null && mtp.matdlist.size() > 0)
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
					"无需上送附加的业务类型!");

		// 按商户检查消费类限制
		chkMerchantPaidLimit(mtp, merchant);

		// 产生交易流水号
		generateMerchantTranSeqNo(merchant, mtp);

		// 记载交易流水

		// 创建交易流水一般性信息
		MchTradDetail mtd = createMchTradDetail(mtp);

		// 支付交易特定信息
		mtd.setTranCode(MchTradDetail.TRANCODE_PAID);
		mtd.setAmount(mtp.totalAmount);
		mtd.setScorePaidAmount(mtp.scorePaidAmount);

		// 记载附加产品信息
		createMchAddTradDetail(mtp);

		// 记载支付附加信息
		createMchAddTradSummary(mtp);

		// 创建附加卡详细信息
		createMchAddCardDetail(mtp);

		return (mtd);
	}

	/**
	 * 记载卡记帐明细
	 * 
	 * @param mtd
	 * @param paidResult
	 * @throws PCPSApplicationException
	 */
	private void createMchAccDetail(MchTradDetail mtd, PaidResult paidResult)
			throws PCPSApplicationException {
		MchAccDetail mad;
		Integer seq = 0;
		for (PaidResult.AccountDetail ad : paidResult.acclist) {
			mad = new MchAccDetail();

			mad.setAccSeqNo(paidResult.accseqno);
			mad.setAccSubSeqNo(seq);
			mad.setMerchantNo(mtd.getMerchantNo());
			mad.setTranDate(mtd.getTranDate());
			mad.setTranSeqNo(mtd.getTranSeqNo());
			mad.setCardNo(ad.CardNo);
			mad.setMasterAccNo(ad.MasterAccNo);
			mad.setSubAccNo(ad.SubAccNo);
			mad.setOpenBranchNo(ad.OpenBranchNo);
			mad.setSalesGroupId(ad.SalesGroupId);
			mad.setPaidAmount(ad.Amount);
			mad.setDepositAmount(BigInteger.ZERO);
			// 注意,消费时手续费需要由其他子系统计算得出
			mad.setFeeAmount(ad.feeAmount);

			seq++;
			mchAccDetailDaoImpl.insert(mad);
		}
	}

	/**
	 * 将卡系统支付成功后的卡系统余额转换到商户支付
	 * 
	 * @param mpc
	 * @param cpc
	 */
	private void setBalanceList(MrchTransParam mtp, PaidResult paidResult,
			Map<String, BigInteger> cardfeemap) {
		Map<String, MrchTransParam.CardAccount> allret = mtp.cardAccMap;
		for (Map.Entry<String, BigInteger> entry : paidResult.cardbalance
				.entrySet()) {
			MrchTransParam.CardAccount ca = allret.remove(entry.getKey());
			if (ca != null) {
				ca.balance = entry.getValue();
				ca.fee = cardfeemap.get(ca.cardNo);
				if (ca.fee == null)
					ca.fee = BigInteger.ZERO;
				allret.put(entry.getKey(), ca);
			}
		}
		mtp.cardAccMap = allret;
	}

	private static final String SCORE_LABEL = "SCORE";
	private static final String OTHER_LABEL = "OTHER";
	private static final String NOBSTYPE_LABEL = "NOBSTYPE";

	/**
	 * 计算Q值最大的1个卡号
	 * 
	 * @param alreadypaid
	 * @param allcardamt
	 * @return
	 */
	private String getMaxQValue(Map<String, BigInteger> alreadypaid,
			Map<String, BigInteger> allcardamt) {
		BigDecimal curmax = BigDecimal.ZERO;
		String cardNo = null;

		for (Map.Entry<String, BigInteger> allentry : allcardamt.entrySet()) {
			String cardno = allentry.getKey();
			BigInteger allamt = allentry.getValue();
			BigInteger allseat = alreadypaid.get(cardno);
			if (allseat == null)
				allseat = BigInteger.ZERO;
			allseat = allseat.add(BigInteger.ONE);
			BigDecimal allamt_q = new BigDecimal(allamt.toString());
			allamt_q = allamt_q.multiply(allamt_q);
			BigDecimal seat_q = new BigDecimal(allseat.toString())
					.multiply(new BigDecimal(allseat.add(BigInteger.ONE)
							.toString()));
			BigDecimal cmax = allamt_q
					.divide(seat_q, 6, RoundingMode.HALF_DOWN);
			if (cmax.compareTo(curmax) > 0) {
				cardNo = cardno;
				curmax = cmax;
			}
		}

		return cardNo;
	}

	/**
	 * 将指定业务类型发生额公平分配到map中
	 * 
	 * @param businessType
	 *            业务类型
	 * @param bsamt
	 *            业务类型发生额
	 * @param allcardamt
	 *            卡号发生额( "SCORE" || "OTHER" || "CARDNO" )
	 */
	private Map<String, BigInteger> distributePaid(String businessType,
			BigInteger bsamt, Map<String, BigInteger> allcardamt) {

		Map<String, BigInteger> alreadypaid = new HashMap<String, BigInteger>();

		// 删除为0的入口数据
		if (allcardamt.size() == 0)
			return alreadypaid;

		// 计算allcardamt的总和
		BigInteger total = BigInteger.ZERO;

		for (Map.Entry<String, BigInteger> tot : allcardamt.entrySet())
			total = total.add(tot.getValue());

		if (total.compareTo(BigInteger.ZERO) <= 0)
			return alreadypaid;

		// 先平均分配整数部分
		// 本笔业务发生额折算到每分钱的贡献度
		BigDecimal bd = new BigDecimal(bsamt.toString()).divide(new BigDecimal(
				total.toString()), 10, RoundingMode.HALF_DOWN);

		for (Map.Entry<String, BigInteger> allentry : allcardamt.entrySet()) {
			String cardno = allentry.getKey();
			BigInteger cardamt = allentry.getValue();
			BigInteger curamt = bd.multiply(new BigDecimal(cardamt.toString()))
					.setScale(0, RoundingMode.HALF_UP).toBigInteger();
			// 折算后不足1分钱,继续
			if (curamt.compareTo(BigInteger.ZERO) <= 0)
				continue;
			BigInteger oldamt = alreadypaid.remove(cardno);
			if (oldamt == null)
				oldamt = BigInteger.ZERO;
			if (cardamt.compareTo(oldamt.add(curamt)) < 0)
				curamt = cardamt.subtract(oldamt);
			curamt = curamt.compareTo(bsamt) <= 0 ? curamt : bsamt;
			oldamt = oldamt.add(curamt);
			bsamt = bsamt.subtract(curamt);
			alreadypaid.put(cardno, oldamt);
			// 如果已经分配完毕
			if (bsamt.compareTo(BigInteger.ZERO) <= 0)
				break;
		}

		// 分配剩余部分,按Q值法,每次分配1分钱
		while (bsamt.compareTo(BigInteger.ZERO) > 0) {
			String cardno = getMaxQValue(alreadypaid, allcardamt);
			BigInteger oldamt = alreadypaid.remove(cardno);
			if (oldamt == null)
				oldamt = BigInteger.ZERO;
			oldamt = oldamt.add(BigInteger.ONE);
			alreadypaid.put(cardno, oldamt);
			bsamt = bsamt.subtract(BigInteger.ONE);
		}

		// 对alreadypaid中的每个记录,将allcardamt中的记录减去
		for (Map.Entry<String, BigInteger> allentry : alreadypaid.entrySet()) {
			String cardno = allentry.getKey();
			BigInteger amt = allentry.getValue();
			BigInteger oldamt = allcardamt.remove(cardno);
			if (oldamt == null)
				continue;
			oldamt = oldamt.subtract(amt);
			if (oldamt.compareTo(BigInteger.ZERO) <= 0)
				continue;
			allcardamt.put(cardno, oldamt);
		}

		return alreadypaid;
	}

	/**
	 * 密码是否为空
	 * 
	 * @param pwd
	 * @return
	 */
	private boolean isEmptyPwd(String pwd) {
		if (PCPSUtil.isEmpty(pwd))
			return true;
		// 全0视为无密码
		for (char ch : pwd.toCharArray()) {
			if (ch != '0')
				return false;
		}
		return true;
	}

	/**
	 * 取真实的密码
	 * 
	 * @param mtp
	 * @param cardNo
	 * @param pwd
	 * @param amt
	 * @return
	 */
	private String getRealPwd(MrchTransParam mtp, String cardNo, String pwd,
			BigInteger amt) throws PCPSApplicationException {
		boolean isneed = merchantSecretPolicyBusinessImpl.isCardHolderNeedPwd(
				mtp.merchantNo, mtp.mrchBrchNo, mtp.tranDate, cardNo, amt);
		if (isneed) {
			if (PCPSUtil.isEmpty(pwd))
				throw new TranApplicationException(TuxedoRespCode.RC_PWDERR,
						"卡" + cardNo + "密码错误!");
			return pwd;
		}
		if (isEmptyPwd(pwd))
			return null;
		return pwd;
	}

	/**
	 * 执行卡系统支付
	 * 
	 * @param mtp
	 * @return
	 * @throws PCPSApplicationException
	 */
	private PaidResult CardPaid(MrchTransParam mtp, MchTradDetail mtd)
			throws PCPSApplicationException {

		// 非预付卡支付
		if (mtp.cardAccMap == null || mtp.cardAccMap.size() < 1)
			return (null);

		// Global
		// Global global = globalDaoImpl.getGlobal();

		// 执行卡支付
		PaidParam paidParam = new PaidParam();

		// 发卡商
		paidParam.issueCompanyNo = mtp.issueCompanyNo;

		// 商户号
		paidParam.merchantNo = mtp.merchantNo;

		// 帐务日期
		paidParam.tranDate = mtp.tranDate;

		// 交易系统流水号 -- 每商户每日唯一
		paidParam.tranSeqNo = mtp.tranSeqNo;

		// 操作员ID
		paidParam.operId = mtp.OperId;

		// 被取消的帐户子系统流水号
		paidParam.cancelAccSeqNo = null;

		// 设置卡记帐信息
		paidParam.allPaidCardList.clear();

		// matdlist
		BigInteger allamt = mtp.totalAmount.add(mtp.scorePaidAmount).add(
				mtp.otherPaidAmount);
		// 每种业务类型与发生额的映射
		Map<String, BigInteger> allbsamt = new HashMap<String, BigInteger>();
		// 每个卡号与发生额的映射
		Map<String, BigInteger> allcardamt = new HashMap<String, BigInteger>();

		// 积分消费额
		if (BigInteger.ZERO.compareTo(mtp.scorePaidAmount) < 0)
			allcardamt.put(SCORE_LABEL, mtp.scorePaidAmount);

		// 其他消费额
		if (BigInteger.ZERO.compareTo(mtp.otherPaidAmount) < 0)
			allcardamt.put(OTHER_LABEL, mtp.otherPaidAmount);

		// 预付卡消费额
		for (Map.Entry<String, MrchTransParam.CardAccount> entry : mtp.cardAccMap
				.entrySet()) {
			MrchTransParam.CardAccount ca = entry.getValue();
			allcardamt.put(ca.cardNo, ca.amount);
		}

		if (mtp.matdlist == null || mtp.matdlist.size() < 1)
			allbsamt.put(NOBSTYPE_LABEL, allamt);
		else {
			for (MchAddTradDetail matd : mtp.matdlist)
				allbsamt.put(matd.getMrchProdId(), matd.getMrchProdAmount());
		}

		Map<String, BigInteger> cardfeemap = new HashMap<String, BigInteger>();
		List<MerchantCardFee> mcflist = new ArrayList<MerchantCardFee>();

		// MAP(业务类型,MAP<种类(预付卡,积分卡,其他),金额>)
		Map<String, Map<String, BigInteger>> allbsmap = new HashMap<String, Map<String, BigInteger>>();

		// 将业务类型的发生额映射到卡上
		for (Map.Entry<String, BigInteger> bsentry : allbsamt.entrySet()) {
			String businessType = bsentry.getKey();
			if (PCPSUtil.compareObject(businessType, NOBSTYPE_LABEL))
				businessType = null;
			Map<String, BigInteger> cardamtmap = distributePaid(bsentry
					.getKey(), bsentry.getValue(), allcardamt);
			allbsmap.put(bsentry.getKey(), cardamtmap);
			// 对cardamtmap中的每一个入口计算该卡消费该业务类型的费用
			for (Map.Entry<String, BigInteger> camentry : cardamtmap.entrySet()) {
				String cardno = camentry.getKey();
				if (PCPSUtil.compareObject(cardno, SCORE_LABEL))
					continue;
				if (PCPSUtil.compareObject(cardno, OTHER_LABEL))
					continue;
				MerchantCardFee mcf = merchantFeePolicyBusinessImpl
						.calcCardHolderFee(mtp.merchantNo, mtp.mrchBrchNo,
								mtp.tranDate,
								MerchantFeePolicy.TRANTYPE_CONSUME,
								businessType, cardno, camentry.getValue());
				PCPSUtil
						.Log(String
								.format(
										"MRCH[%s - %s]CARDNO[%s]BUSINESSTYPE[%s]AMT[%s]FEERATIO[%s]FEE[%s]",
										mtp.merchantNo, mtp.mrchBrchNo, cardno,
										businessType == null ? "NULL"
												: businessType, camentry
												.getValue().toString(),
										mcf.feeRatio.toString(), mcf.fee
												.toString()));
				mcf.cardNo = cardno;
				mcf.businessType = businessType;
				mcf.amt = camentry.getValue();
				mcflist.add(mcf);
				BigInteger lastamt = cardfeemap.remove(cardno);
				if (lastamt == null)
					lastamt = BigInteger.ZERO;
				cardfeemap.put(cardno, lastamt.add(mcf.fee));
			}
		}

		// 创建附加交易类型详细信息
		createMchBsTypeTradDetail(mtp, allbsmap);

		BigInteger sumfee = BigInteger.ZERO;

		// 逐笔设置记帐卡信息
		for (Map.Entry<String, MrchTransParam.CardAccount> entry : mtp.cardAccMap
				.entrySet()) {
			MrchTransParam.CardAccount ca = entry.getValue();
			PaidParam.PaidCardParam pcp = paidParam.new PaidCardParam();
			pcp.cardNo = ca.cardNo;
			pcp.pwd = getRealPwd(mtp, ca.cardNo, ca.cardPwd, ca.amount);
			pcp.amt = ca.amount;
			pcp.fee = cardfeemap.get(ca.cardNo);
			if (pcp.fee == null)
				pcp.fee = BigInteger.ZERO;
			/* yxb 2013.07.04 修正 消费之后余额不减反增 */
			if (BigInteger.ZERO.compareTo(pcp.fee) > 0
					&& pcp.fee.abs().compareTo(ca.amount) >= 0)
				throw new CardTranApplicationException(
						TuxedoRespCode.RC_AMOUNT, String.format(
								"卡%s发生额%s手续费%s无效", ca.cardNo, ca.amount
										.toString(), pcp.fee.toString()));
			sumfee = sumfee.add(pcp.fee);
			paidParam.allPaidCardList.add(pcp);
		}

		// 请求卡系统记帐
		PaidResult paidResult = cardTransBusinessImpl.Paid(paidParam);

		// 记载卡记帐明细
		createMchAccDetail(mtd, paidResult);

		// 设置交易流水关于卡系统的记帐流水号
		mtd.setAccAckSeqNo(paidResult.accseqno);

		// 设置合计手续费或折扣
		mtd.setFee(sumfee);

		// 向client端设置返回的余额列表
		setBalanceList(mtp, paidResult, cardfeemap);

		// 默认返回的积分发生额&积分余额全部置0
		mtp.scoreAmount = BigInteger.ZERO;
		mtp.score = BigInteger.ZERO;

		// 根据会员卡号进行积分
		// ScoreAccountCommon sac = ScoreAccount(mpc);

		/*
		 * if (sac != null) { // 返回积分余额
		 * mpc.setScoreAmount(sac.getScoreAmount());
		 * mpc.setScore(sac.getScore()); // 交易明细保存积分系统返回数据项
		 * mtd.setMembershipId(sac.getCustomId());
		 * mtd.setScoreTranSeqNo(sac.getTranSeqNo());
		 * mtd.setScoreAmount(sac.getScoreAmount());
		 * mtd.setScore(sac.getScore()); }
		 */

		return (paidResult);
	}

	/**
	 * 执行卡系统支付取消
	 * 
	 * @param mpc
	 * @return
	 * @throws POSTApplicationException
	 */
	private PaidResult CardPaidCancel(MrchTransParam mtp,
			MchTradDetail origmtd, MchTradDetail mtd)
			throws PCPSApplicationException {
		// Global
		// Global global = globalDaoImpl.getGlobal();

		// 执行卡支付
		PaidParam paidParam = new PaidParam();

		// 发卡商
		paidParam.issueCompanyNo = mtp.issueCompanyNo;

		// 商户号
		paidParam.merchantNo = mtp.merchantNo;

		// 帐务日期
		paidParam.tranDate = mtp.tranDate;

		// 交易系统流水号 -- 每商户每日唯一
		paidParam.tranSeqNo = mtp.tranSeqNo;

		// 操作员ID
		paidParam.operId = mtp.OperId;

		// 被取消的帐户子系统流水号
		paidParam.cancelAccSeqNo = origmtd.getAccAckSeqNo();

		// 设置卡记帐信息
		paidParam.allPaidCardList.clear();

		// 请求卡系统取消记帐
		PaidResult paidResult = cardTransBusinessImpl.PaidCancel(paidParam);

		// 返回记帐结果
		mtd.setAccAckSeqNo(paidResult.accseqno);

		// 记载详细的记帐明细信息
		createMchAccDetail(origmtd, mtd);

		return paidResult;
	}

	/**
	 * 卡消费
	 * 
	 * @throws PCPSApplicationException
	 */
	public MrchTransParam Paid(MrchTransParam mtp)
			throws PCPSApplicationException {
		// 验证并重设交易时间
		validateTranTime(mtp);

		// 交易检查
		checkPaidArgs(mtp);

		// 执行本地业务逻辑
		MchTradDetail mtd = LocalPaid(mtp);

		// 预付卡支付
		CardPaid(mtp, mtd);

		// 更新交易明细
		mchTradDetailDaoImpl.insert(mtd);

		return (mtp);
	}

	/**
	 * 执行卡系统支付试算费
	 * 
	 * @param mtp
	 * @return
	 * @throws PCPSApplicationException
	 */
	private void CardPaidCalcFee(MrchTransParam mtp)
			throws PCPSApplicationException {

		// 非预付卡支付
		if (mtp.cardAccMap == null || mtp.cardAccMap.size() < 1)
			return;

		// matdlist
		BigInteger allamt = mtp.totalAmount.add(mtp.scorePaidAmount).add(
				mtp.otherPaidAmount);
		// 每种业务类型与发生额的映射
		Map<String, BigInteger> allbsamt = new HashMap<String, BigInteger>();
		// 每个卡号与发生额的映射
		Map<String, BigInteger> allcardamt = new HashMap<String, BigInteger>();

		// 积分消费额
		if (BigInteger.ZERO.compareTo(mtp.scorePaidAmount) < 0)
			allcardamt.put(SCORE_LABEL, mtp.scorePaidAmount);

		// 其他消费额
		if (BigInteger.ZERO.compareTo(mtp.otherPaidAmount) < 0)
			allcardamt.put(OTHER_LABEL, mtp.otherPaidAmount);

		// 预付卡消费额
		for (Map.Entry<String, MrchTransParam.CardAccount> entry : mtp.cardAccMap
				.entrySet()) {
			MrchTransParam.CardAccount ca = entry.getValue();
			allcardamt.put(ca.cardNo, ca.amount);
		}

		if (mtp.matdlist == null || mtp.matdlist.size() < 1)
			allbsamt.put(NOBSTYPE_LABEL, allamt);
		else {
			for (MchAddTradDetail matd : mtp.matdlist)
				allbsamt.put(matd.getMrchProdId(), matd.getMrchProdAmount());
		}

		Map<String, BigInteger> cardfeemap = new HashMap<String, BigInteger>();
		List<MerchantCardFee> mcflist = new ArrayList<MerchantCardFee>();

		// MAP(业务类型,MAP<种类(预付卡,积分卡,其他),金额>)
		Map<String, Map<String, BigInteger>> allbsmap = new HashMap<String, Map<String, BigInteger>>();

		// 将业务类型的发生额映射到卡上
		for (Map.Entry<String, BigInteger> bsentry : allbsamt.entrySet()) {
			String businessType = bsentry.getKey();
			if (PCPSUtil.compareObject(businessType, NOBSTYPE_LABEL))
				businessType = null;
			Map<String, BigInteger> cardamtmap = distributePaid(bsentry
					.getKey(), bsentry.getValue(), allcardamt);
			allbsmap.put(bsentry.getKey(), cardamtmap);
			// 对cardamtmap中的每一个入口计算该卡消费该业务类型的费用
			for (Map.Entry<String, BigInteger> camentry : cardamtmap.entrySet()) {
				String cardno = camentry.getKey();
				if (PCPSUtil.compareObject(cardno, SCORE_LABEL))
					continue;
				if (PCPSUtil.compareObject(cardno, OTHER_LABEL))
					continue;
				MerchantCardFee mcf = merchantFeePolicyBusinessImpl
						.calcCardHolderFee(mtp.merchantNo, mtp.mrchBrchNo,
								mtp.tranDate,
								MerchantFeePolicy.TRANTYPE_CONSUME,
								businessType, cardno, camentry.getValue());
				PCPSUtil
						.Log(String
								.format(
										"MRCH[%s - %s]CARDNO[%s]BUSINESSTYPE[%s]AMT[%s]FEERATIO[%s]FEE[%s]",
										mtp.merchantNo, mtp.mrchBrchNo, cardno,
										businessType == null ? "NULL"
												: businessType, camentry
												.getValue().toString(),
										mcf.feeRatio.toString(), mcf.fee
												.toString()));
				mcf.cardNo = cardno;
				mcf.businessType = businessType;
				mcf.amt = camentry.getValue();
				mcflist.add(mcf);
				BigInteger lastamt = cardfeemap.remove(cardno);
				if (lastamt == null)
					lastamt = BigInteger.ZERO;
				cardfeemap.put(cardno, lastamt.add(mcf.fee));
			}
		}

		// 创建附加交易类型详细信息
		// createMchBsTypeTradDetail(mtp, allbsmap);

		BigInteger sumfee = BigInteger.ZERO;

		Map<String, MrchTransParam.CardAccount> allret = new HashMap<String, MrchTransParam.CardAccount>();

		// 逐笔设置记帐卡信息
		for (Map.Entry<String, MrchTransParam.CardAccount> entry : mtp.cardAccMap
				.entrySet()) {
			MrchTransParam.CardAccount ca = entry.getValue();
			MrchTransParam.CardAccount newca = mtp.new CardAccount();
			newca.cardNo = ca.cardNo;
			newca.amount = ca.amount;
			newca.fee = cardfeemap.get(ca.cardNo);
			if (newca.fee == null)
				newca.fee = BigInteger.ZERO;
			/* yxb 2013.07.04 修正 消费之后余额不减反增 */
			if (BigInteger.ZERO.compareTo(newca.fee) > 0
					&& newca.fee.abs().compareTo(ca.amount) >= 0)
				throw new CardTranApplicationException(
						TuxedoRespCode.RC_AMOUNT, String.format(
								"卡%s发生额%s手续费%s无效", ca.cardNo, ca.amount
										.toString(), newca.fee.toString()));
			sumfee = sumfee.add(newca.fee);
		}

		mtp.cardAccMap = allret;

		// 默认返回的积分发生额&积分余额全部置0
		mtp.scoreAmount = BigInteger.ZERO;
		mtp.score = BigInteger.ZERO;
	}

	/**
	 * 卡消费试算费
	 * 
	 * @throws PCPSApplicationException
	 */
	public MrchTransParam PaidCalcFee(MrchTransParam mtp)
			throws PCPSApplicationException {
		// 验证并重设交易时间
		validateTranTime(mtp);

		// 交易检查
		checkPaidArgs(mtp);

		// 商户支付检查
		Merchant merchant = chkMerchantCommonForBusiness(mtp);

		// 如果要求商户在消费时附带业务类型但未上送,则异常
		if (PCPSUtil.compareObject(merchant.getNeedBusinessType(),
				Merchant.BUSINESSTYPE_NEED)
				&& (mtp.matdlist == null || mtp.matdlist.size() < 1))
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
					"需要上送附加的业务类型!");
		else if (!PCPSUtil.compareObject(merchant.getNeedBusinessType(),
				Merchant.BUSINESSTYPE_NEED)
				&& mtp.matdlist != null && mtp.matdlist.size() > 0)
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
					"无需上送附加的业务类型!");

		// 预付卡支付试算费
		CardPaidCalcFee(mtp);

		return (mtp);
	}

	/**
	 * 支付冲正/撤销业务入口参数检查
	 * 
	 * @param mtp
	 *            商户支付基础信息
	 */
	private void checkPaidCancelCorrArgs(MrchTransParam mtp)
			throws PCPSApplicationException {

		// 检查业务参数
		checkPaidBusinessArgs(mtp);

		// 请求流水号检查
		if (PCPSUtil.isEmpty(mtp.mrchReqSeqNo))
			throw new TranApplicationException(TuxedoRespCode.RC_REQSEQNO,
					"请求流水号为空!");

		// 合计发生额检查 -- 撤销或冲正仅检查合计发生额(预付卡+积分卡)
		if (BigInteger.ZERO.compareTo(mtp.totalAmount) >= 0)
			throw new TranApplicationException(TuxedoRespCode.RC_AMOUNT,
					"金额无效 !");
	}

	/**
	 * 本地支付撤销业务逻辑
	 * 
	 * @param mtp
	 *            商户支付冲正基础信息
	 * @return MchTradDetail
	 */
	private MchTradDetail LocalPaidCancel(MrchTransParam mtp,
			MchTradDetail origmtd) throws PCPSApplicationException {
		// 商户支付检查
		Merchant merchant = chkMerchantCommonForBusiness(mtp);

		// 商户请求流水号唯一性检查
		checkMrchSeqNo(mtp);

		// 产生交易流水号
		generateMerchantTranSeqNo(merchant, mtp);

		// 置原交易的撤销流水号
		origmtd.setOtherTranSeqNo(mtp.tranSeqNo);
		origmtd.setOtherTranDate(mtp.tranDate);
		origmtd.setOtherMrchSeqNo(mtp.mrchReqSeqNo);

		// 更新原交易流水
		mchTradDetailDaoImpl.update(origmtd);

		// 记载交易流水

		// 创建交易流水一般性信息 -- 根据被撤销交易流水号
		MchTradDetail mtd = createMchTradDetailByOrig(mtp, origmtd);

		// 支付交易特定信息
		mtd.setTranCode(MchTradDetail.TRANCODE_PAID_CANCEL);
		mtd.setAmount(BigInteger.ZERO.subtract(origmtd.getAmount()));
		mtd.setFee(BigInteger.ZERO.subtract(origmtd.getFee()));
		mtd.setScorePaidAmount(BigInteger.ZERO.subtract(origmtd
				.getScorePaidAmount()));

		// 置原交易流水号
		mtd.setOtherTranSeqNo(origmtd.getTranSeqNo());
		mtd.setOtherTranDate(origmtd.getTranDate());
		mtd.setOtherMrchSeqNo(origmtd.getMrchSeqNo());

		// 记载附加产品信息 -- 根据原交易
		createMchAddTradDetailByOrig(mtp, origmtd);

		// 记载支付附加信息 -- 根据原交易
		createMchAddTradSummaryByOrig(mtp, origmtd);

		// 创建附加卡详细信息 -- 根据原交易
		createMchAddCardDetailByOrig(mtp, origmtd);

		return (mtd);
	}

	/**
	 * 卡消费取消/退货
	 * 
	 * @throws PCPSApplicationException
	 */
	public MrchTransParam PaidCancel(MrchTransParam mtp)
			throws PCPSApplicationException {

		// 假定被取消流水位于当前表中
		boolean ishis = false;

		// 支付冲正/撤销业务入口参数检查
		checkPaidCancelCorrArgs(mtp);

		// 按照商户端发来的条件检索业务流水
		List<MchTradDetail> mtdlist = mchTradDetailDaoImpl
				.getMchTradDetailByMrchSeqNo(mtp.merchantNo,
						mtp.cancelTranDate, mtp.cancelMrchReqSeqNo);
		// 如果不存在,则从历史表查找,如仍不存在,则抛出异常
		if (mtdlist == null || mtdlist.size() == 0) {
			mtdlist = mchTradDetailDaoImpl
					.getMchTradDetailByMrchSeqNoFromHistory(mtp.merchantNo,
							mtp.cancelTranDate, mtp.cancelMrchReqSeqNo);
			if (mtdlist == null || mtdlist.size() != 1)
				throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
						"被撤销流水不存在或者多于一条!");
			ishis = true;
		}

		if (mtdlist.size() != 1)
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被撤销流水多于一条!");

		// 取原交易
		MchTradDetail origmtd = mtdlist.get(0);

		// 原交易必须是支付交易
		if (!PCPSUtil.compareObject(MchTradDetail.TRANCODE_PAID, origmtd
				.getTranCode()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被撤销交易不是支付交易!");

		// 流水号必须一致
		if (!PCPSUtil
				.compareObject(origmtd.getTranSeqNo(), mtp.cancelTranSeqNo))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被撤销交易的主机流水号不一致," + origmtd.getTranSeqNo() + " <> "
							+ mtp.cancelTranSeqNo);

		// 如原交易已经被冲正
		if (!PCPSUtil.isEmpty(origmtd.getCancelTranSeqNo()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被撤销交易已被冲正!");

		// 如原交易已经做撤销/退货处理 -- 返回交易不存在
		if (!PCPSUtil.isEmpty(origmtd.getOtherMrchSeqNo()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被撤销交易已经被撤销或者退货!");

		// 检查发生额,撤销时的发生额合计必须为原支付交易的预付卡支付额+会员卡支付额
		BigInteger totalAmount = origmtd.getAmount().add(
				origmtd.getScorePaidAmount());
		if (!totalAmount.equals(mtp.totalAmount))
			throw new TranApplicationException(TuxedoRespCode.RC_DISCORD,
					"被撤销交易合计发生额不一致," + totalAmount.toString() + " <> "
							+ mtp.totalAmount.toString());

		if (ishis) {
			// 将该流水从历史表中删除
			PCPSUtil.Log("migrateMchTradDetailFromHistory");
			mchTradDetailDaoImpl.migrateMchTradDetailFromHistory(origmtd
					.getMerchantNo(), origmtd.getTranDate(), origmtd
					.getTranSeqNo());
			PCPSUtil.Log("BEGIN getMchTradDetailByMrchSeqNo");
			mtdlist = mchTradDetailDaoImpl.getMchTradDetailByMrchSeqNo(
					mtp.merchantNo, mtp.cancelTranDate, mtp.cancelMrchReqSeqNo);
			if (mtdlist == null || mtdlist.size() != 1)
				throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
						"迁移历史流水记录到当日错误!");
			origmtd = mtdlist.get(0);
			PCPSUtil.Log("getMchTradDetailByMrchSeqNo OK!!!");
		}

		// 本地撤销
		MchTradDetail mtd = LocalPaidCancel(mtp, origmtd);

		// 执行积分支付取消
		// if (!POSTUtil.isEmpty(origmtd.getScorePaidTranSeqNo()))
		// ScorePaidCancel(origmtd, mtd);

		// 执行卡支付取消,如果原交易产生了积分,则同时冲正积分的产生
		if (!PCPSUtil.isEmpty(origmtd.getAccAckSeqNo()))
			CardPaidCancel(mtp, origmtd, mtd);

		// 记载冲正交易流水
		mchTradDetailDaoImpl.insert(mtd);

		return mtp;
	}

	/**
	 * 本地支付冲正业务逻辑
	 * 
	 * @param mtp
	 *            商户支付冲正基础信息
	 * @return MchTradDetail
	 */
	private MchTradDetail LocalPaidCorr(MrchTransParam mtp,
			MchTradDetail origmtd) throws PCPSApplicationException {
		// 商户支付检查
		Merchant merchant = chkMerchantCommonForBusiness(mtp);

		// 产生交易流水号
		generateMerchantTranSeqNo(merchant, mtp);

		// 置原交易的冲正流水号
		origmtd.setCancelTranSeqNo(mtp.tranSeqNo);

		// 更新原交易流水
		mchTradDetailDaoImpl.update(origmtd);

		// 记载交易流水

		// 创建交易流水一般性信息 -- 根据被冲正交易流水号
		MchTradDetail mtd = createMchTradDetailByOrig(mtp, origmtd);

		mtd.setMrchSeqNo(mtp.mrchReqSeqNo + "_C");

		// 支付交易特定信息
		mtd.setTranCode(MchTradDetail.TRANCODE_PAID_CORR);
		mtd.setAmount(BigInteger.ZERO.subtract(origmtd.getAmount()));
		mtd.setFee(BigInteger.ZERO.subtract(origmtd.getFee()));
		mtd.setScorePaidAmount(BigInteger.ZERO.subtract(origmtd
				.getScorePaidAmount()));

		// 置原交易流水号
		mtd.setCancelTranSeqNo(origmtd.getTranSeqNo());

		// 记载附加产品信息 -- 根据原交易
		createMchAddTradDetailByOrig(mtp, origmtd);

		// 记载支付附加信息 -- 根据原交易
		createMchAddTradSummaryByOrig(mtp, origmtd);

		// 创建附加卡详细信息 -- 根据原交易
		createMchAddCardDetailByOrig(mtp, origmtd);

		return (mtd);
	}

	/**
	 * 卡消费冲正
	 * 
	 * @throws PCPSApplicationException
	 */
	public MrchTransParam PaidCorrect(MrchTransParam mtp)
			throws PCPSApplicationException {

		// 支付冲正/撤销业务入口参数检查
		checkPaidCancelCorrArgs(mtp);

		// 按照商户端发来的条件检索业务流水
		List<MchTradDetail> mtdlist = mchTradDetailDaoImpl
				.getMchTradDetailByMrchSeqNo(mtp.merchantNo, mtp.tranDate,
						mtp.mrchReqSeqNo);
		// 如果不存在,则返回成功
		if (mtdlist == null || mtdlist.size() < 1)
			return mtp;

		if (mtdlist.size() > 1)
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被冲正流水多于一条!");

		// 取原交易
		MchTradDetail origmtd = mtdlist.get(0);

		// 原交易必须是支付交易
		if (!PCPSUtil.compareObject(MchTradDetail.TRANCODE_PAID, origmtd
				.getTranCode()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被冲正交易不是支付交易!");

		// 如原交易已经被冲正 -- 返回成功
		if (!PCPSUtil.isEmpty(origmtd.getCancelTranSeqNo()))
			return mtp;

		// 如原交易已经做撤销/退货处理 -- 返回交易不存在
		if (!PCPSUtil.isEmpty(origmtd.getOtherMrchSeqNo()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被冲正交易已经被撤销或者退货!");

		// 检查发生额,冲正时的发生额合计必须为原支付交易的预付卡支付额+会员卡支付额
		BigInteger totalAmount = origmtd.getAmount().add(
				origmtd.getScorePaidAmount());
		if (!totalAmount.equals(mtp.totalAmount))
			throw new TranApplicationException(TuxedoRespCode.RC_DISCORD,
					"冲正交易合计发生额不一致," + totalAmount.toString() + " <> "
							+ mtp.totalAmount.toString());

		// 本地冲正
		MchTradDetail mtd = LocalPaidCorr(mtp, origmtd);

		// 执行积分支付取消
		// if (!POSTUtil.isEmpty(origmtd.getScorePaidTranSeqNo()))
		// ScorePaidCancel(origmtd, mtd);

		// 执行卡支付取消,如果原交易产生了积分,则同时冲正积分的产生
		if (!PCPSUtil.isEmpty(origmtd.getAccAckSeqNo()))
			CardPaidCancel(mtp, origmtd, mtd);

		// 记载冲正交易流水
		mchTradDetailDaoImpl.insert(mtd);

		return mtp;
	}

	/**
	 * 本地支付撤销冲正业务逻辑
	 * 
	 * @param mtp
	 *            商户支付撤销冲正基础信息
	 * @return MchTradDetail
	 */
	private MchTradDetail LocalPaidCancelCorr(MrchTransParam mtp,
			MchTradDetail origmtd) throws PCPSApplicationException {
		// 商户支付检查
		Merchant merchant = chkMerchantCommonForBusiness(mtp);

		// 产生交易流水号
		generateMerchantTranSeqNo(merchant, mtp);

		// 置原交易的冲正流水号
		origmtd.setCancelTranSeqNo(mtp.tranSeqNo);

		// 更新原交易流水
		mchTradDetailDaoImpl.update(origmtd);

		// 记载交易流水

		// 创建交易流水一般性信息 -- 根据被冲正交易流水号
		MchTradDetail mtd = createMchTradDetailByOrig(mtp, origmtd);

		mtd.setMrchSeqNo(mtp.mrchReqSeqNo + "_C");

		// 支付交易特定信息
		mtd.setTranCode(MchTradDetail.TRANCODE_PAID_CANCEL_CORR);
		mtd.setAmount(BigInteger.ZERO.subtract(origmtd.getAmount()));
		mtd.setFee(BigInteger.ZERO.subtract(origmtd.getFee()));
		mtd.setScorePaidAmount(BigInteger.ZERO.subtract(origmtd
				.getScorePaidAmount()));

		// 置原交易流水号
		mtd.setCancelTranSeqNo(origmtd.getTranSeqNo());

		// 记载附加产品信息 -- 根据原交易
		createMchAddTradDetailByOrig(mtp, origmtd);

		// 记载支付附加信息 -- 根据原交易
		createMchAddTradSummaryByOrig(mtp, origmtd);

		// 创建附加卡详细信息 -- 根据原交易
		createMchAddCardDetailByOrig(mtp, origmtd);

		// 重置最初的交易为未被撤销状态
		restoreOrigMchTradDetail(origmtd);

		return (mtd);
	}

	/**
	 * 执行卡系统重做消费记帐
	 * 
	 * @param mtp
	 * @return
	 * @throws PCPSApplicationException
	 */
	private void redoCardPaid(MrchTransParam mtp, MchTradDetail origmtd,
			MchTradDetail mtd) throws PCPSApplicationException {

		// 执行卡支付
		PaidParam paidParam = new PaidParam();

		// 发卡商
		paidParam.issueCompanyNo = mtp.issueCompanyNo;

		// 商户号
		paidParam.merchantNo = mtp.merchantNo;

		// 帐务日期
		paidParam.tranDate = mtp.tranDate;

		// 交易系统流水号 -- 每商户每日唯一
		paidParam.tranSeqNo = mtp.tranSeqNo;

		// 操作员ID
		paidParam.operId = mtp.OperId;

		// 被取消的帐户子系统流水号
		paidParam.redoAccSeqNo = origmtd.getAccAckSeqNo();

		// 请求卡系统取消记帐
		PaidResult paidResult = cardTransBusinessImpl.redoPaid(paidParam);

		// 返回记帐结果
		mtd.setAccAckSeqNo(paidResult.accseqno);

		// 记载详细的记帐明细信息
		createMchAccDetail(origmtd, mtd);
	}

	/**
	 * 卡消费撤销冲正 mtp 商户交易参数对象
	 * 
	 * @throws PCPSApplicationException
	 *             异常信息
	 */
	public MrchTransParam PaidCancelCorr(MrchTransParam mtp)
			throws PCPSApplicationException {
		// 支付撤销冲正业务入口参数检查
		checkPaidCancelCorrArgs(mtp);

		// 按照商户端发来的条件检索业务流水
		List<MchTradDetail> mtdlist = mchTradDetailDaoImpl
				.getMchTradDetailByMrchSeqNo(mtp.merchantNo, mtp.tranDate,
						mtp.mrchReqSeqNo);
		// 如果不存在,则返回成功
		if (mtdlist == null || mtdlist.size() < 1)
			return mtp;

		if (mtdlist.size() > 1)
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被冲正流水多于一条!");

		// 取原交易
		MchTradDetail origmtd = mtdlist.get(0);

		// 原交易必须是支付撤销交易
		if (!PCPSUtil.compareObject(MchTradDetail.TRANCODE_PAID_CANCEL, origmtd
				.getTranCode()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被冲正交易不是支付撤销交易!");

		// 如原交易已经被冲正 -- 返回成功
		if (!PCPSUtil.isEmpty(origmtd.getCancelTranSeqNo()))
			return mtp;

		// 检查发生额,冲正时的发生额合计必须为原支付交易的预付卡支付额+会员卡支付额的相反数
		BigInteger totalAmount = origmtd.getAmount().add(
				origmtd.getScorePaidAmount());
		if (!totalAmount.add(mtp.totalAmount).equals(BigInteger.ZERO))
			throw new TranApplicationException(TuxedoRespCode.RC_DISCORD,
					"冲正交易合计发生额不一致," + totalAmount.toString() + " <> "
							+ mtp.totalAmount.toString());

		// 本地冲正
		MchTradDetail mtd = LocalPaidCancelCorr(mtp, origmtd);

		// 执行积分支付取消
		// if (!POSTUtil.isEmpty(origmtd.getScorePaidTranSeqNo()))
		// ScorePaidCancel(origmtd, mtd);

		// 执行卡支付取消,如果原交易产生了积分,则同时冲正积分的产生
		if (!PCPSUtil.isEmpty(origmtd.getAccAckSeqNo()))
			redoCardPaid(mtp, origmtd, mtd);

		// 记载冲正交易流水
		mchTradDetailDaoImpl.insert(mtd);

		return mtp;
	}

	/**
	 * 充值业务入口参数检查
	 * 
	 * @param mtp
	 *            商户支付基础信息
	 */
	private void checkDepositArgs(MrchTransParam mtp)
			throws PCPSApplicationException {

		// 商户检查
		if (PCPSUtil.isEmpty(mtp.merchantNo))
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					"商户号为空!");

		// 确保分支机构号存在
		MerchantBranch mb = merchantBranchDaoImpl
				.getMerchantBranchByMerchantTrans(mtp.merchantNo,
						mtp.mrchBrchNo);
		if (mb == null)
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					"商户分支机构号" + mtp.mrchBrchNo + "不存在!");

		// 重新进行映射
		mtp.mrchBrchNo = mb.getMerchantBranchNo();

		// 帐务日期检查
		if (mtp.tranDate == null)
			throw new TranApplicationException(TuxedoRespCode.RC_TRANDATE,
					"帐务日期为空!");

		// 请求流水号检查
		if (PCPSUtil.isEmpty(mtp.mrchReqSeqNo))
			throw new TranApplicationException(TuxedoRespCode.RC_REQSEQNO,
					"请求流水号为空!");

		// 检查卡
		if (mtp.depositCard == null || PCPSUtil.isEmpty(mtp.depositCard.cardNo))
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID,
					"记帐卡信息为空!");

		// 发生额检查
		if (mtp.depositCard.amount == null
				|| BigInteger.ZERO.compareTo(mtp.depositCard.amount) >= 0)
			throw new TranApplicationException(TuxedoRespCode.RC_AMOUNT,
					"金额无效 !");
	}

	/**
	 * 根据充值交易请求记载交易流水
	 * 
	 * @param mtd
	 */
	private MchTradDetail createDepositMchTradDetail(MrchTransParam mtp)
			throws PCPSApplicationException {

		MchTradDetail mtd = new MchTradDetail();
		mtd.setMerchantNo(mtp.merchantNo);
		mtd.setTranDate(mtp.tranDate);
		mtd.setTranSeqNo(mtp.tranSeqNo);
		mtd.setCancelTranSeqNo(null);
		mtd.setTranTime(mtp.curSysDateTime);
		mtd.setMrchSeqNo(mtp.mrchReqSeqNo);

		// 充值卡号
		mtd.setCardNo(mtp.depositCard.cardNo);

		// 卡系统相关 -- 暂时置null
		mtd.setAccAckSeqNo(null);

		// 商户业务摘要
		mtd.setMrchAbstract(mtp.mrchAbstract);

		// 其他辅助信息 -- 终端 &机构
		mtd.setTerminalNo(mtp.mrchPosNo);
		mtd.setTermSettBatchNo(mtp.mrchPosBatchNo);
		mtd.setTermSeqNo(mtp.mrchPosSeqNo);
		mtd.setMrchTellerNo(mtp.mrchTellerNo);
		mtd.setMrchBrchNo(mtp.mrchBrchNo);

		return (mtd);
	}

	/**
	 * 获取商户充值的归属销售机构号
	 * 
	 * @param mtp
	 * @throws PCPSApplicationException
	 */
	private void getValidateDepositSaleBranch(MrchTransParam mtp,
			Merchant merchant) throws PCPSApplicationException {
		// 检查发卡商
		IssueCompany issueCompany = issueCompanyDaoImpl
				.getIssueCompanyByIssueCompanyNo(mtp.issueCompanyNo);

		// 检查发卡商是否允许商户处充值
		if (PCPSUtil.compareObject(issueCompany.getAllowMrchDeposit(),
				IssueCompany.ISSUECOMPANY_NOALLOW_DEPOSIT))
			throw new TranApplicationException(TuxedoRespCode.RC_No_Deposit,
					"本发卡商不允许商户充值交易!");

		// 检查是否允许本商户充值
		if (!PCPSUtil.compareObject(merchant.getAllowDeposit(),
				Merchant.MERCHANT_ALLOW_DEPOSIT))
			throw new TranApplicationException(TuxedoRespCode.RC_No_Deposit,
					"本商户不允许充值交易!");

		// 取商户充值归属销售机构号
		// mtp.depositSaleBranchNo = merchant.getDepositSaleBranchNo();

		// 如果商户未设置,则取发卡商
		// if (PCPSUtil.isEmpty(mtp.depositSaleBranchNo))
		// mtp.depositSaleBranchNo = issueCompany.getDepositSaleBranchNo();

		// if (PCPSUtil.isEmpty(mtp.depositSaleBranchNo))
		// throw new TranApplicationException(
		// TuxedoRespCode.RC_No_DepositBranchNo, "未设置商户充值归属销售机构!");

		// 判断机构号的存在性
		// SaleBranch saleBranch = saleBranchDaoImpl
		// .getSaleBranchBySaleBranchNo(mtp.depositSaleBranchNo);
		// if (saleBranch == null
		// || !mtp.depositSaleBranchNo.startsWith(issueCompany
		// .getIssueCompanyNo()))
		// throw new TranApplicationException(
		// TuxedoRespCode.RC_No_DepositBranchNo, "商户充值归属销售机构设置有误!");

		// 单笔限额判断
		BigInteger eachDepositLimit = merchant.getEachDepositLimit();
		if (eachDepositLimit.compareTo(BigInteger.ZERO) == 0)
			eachDepositLimit = BigInteger.valueOf(issueCompany
					.getMrchEachDepositLimit().intValue());

		int result = eachDepositLimit.compareTo(BigInteger.ZERO);
		if (result < 0)
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
					"发卡商或商户单笔充值额度上限参数设置错!");

		if (result > 0
				&& mtp.depositCard.amount.compareTo(eachDepositLimit) > 0)
			throw new TranApplicationException(
					TuxedoRespCode.RC_DepositAmountOverLimit,
					"充值金额超过发卡商或商户单笔充值额度上限参数!");

		// 判断商户单卡单日累计限额
		BigInteger mddl = BigInteger.ZERO;
		if (issueCompany.getMrchDayDepositLimit() != null)
			mddl = BigInteger.valueOf(issueCompany.getMrchDayDepositLimit());
		if (BigInteger.ZERO.compareTo(mddl) > 0) {
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
					"发卡商单卡单日累计充值参数设置错!");
		}

		// 如果设置了有效的参数
		if (BigInteger.ZERO.compareTo(mddl) < 0) {
			BigInteger daytotal = mchTradDetailDaoImpl.getDayDepositByCardNo(
					mtp.depositCard.cardNo, mtp.tranDate);
			daytotal = daytotal.add(mtp.depositCard.amount);
			if (daytotal.compareTo(mddl) > 0) {
				throw new TranApplicationException(
						TuxedoRespCode.RC_DepositAmountOverLimit,
						"卡在商户处累计充值金额超过发卡商单卡单日累计充值参数!");
			}
		}

		// 累计限额判断(等待完善此段代码,2013.05.17已完善)
		BigInteger depositLimitCycle = BigInteger.valueOf(merchant
				.getDepositLimitCycle());
		BigInteger depositLimit = merchant.getDepositLimit();

		// 如果未设置累计充值限额参数则返回
		if (BigInteger.ZERO.compareTo(depositLimitCycle) >= 0
				|| BigInteger.ZERO.compareTo(depositLimit) >= 0)
			return;

		// 计算累计阀值
		MchTradDetail mtd = mchTradDetailDaoImpl
				.getMchTradDetailDayDepositSummary(merchant.getMerchantNo(),
						mtp.tranDate, merchant.getDepositLimitCycle());

		if (mtp.depositCard.amount.add(mtd.getAmount()).compareTo(depositLimit) > 0)
			throw new TranApplicationException(
					TuxedoRespCode.RC_DepositAmountOverCycleLimit,
					"充值金额超过发卡商或商户周期累计充值额度上限参数!");
	}

	/**
	 * 卡充值验证 mtp 商户交易参数对象
	 * 
	 * @throws PCPSApplicationException
	 *             异常信息
	 */
	public MrchTransParam DepositValidate(MrchTransParam mtp)
			throws PCPSApplicationException {
		// 商户检查
		if (PCPSUtil.isEmpty(mtp.merchantNo))
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					"商户号为空!");

		// 帐务日期检查
		if (mtp.tranDate == null)
			throw new TranApplicationException(TuxedoRespCode.RC_TRANDATE,
					"帐务日期为空!");

		// 检查卡
		if (mtp.depositCard == null || PCPSUtil.isEmpty(mtp.depositCard.cardNo))
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID,
					"充值卡信息为空!");

		// 检查发卡商
		IssueCompany issueCompany = issueCompanyDaoImpl
				.getIssueCompanyByIssueCompanyNo(mtp.issueCompanyNo);

		// 检查发卡商是否允许商户处充值
		if (PCPSUtil.compareObject(issueCompany.getAllowMrchDeposit(),
				IssueCompany.ISSUECOMPANY_NOALLOW_DEPOSIT))
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
					"本发卡商不允许商户充值交易!");

		// 验证商户信息
		Merchant merchant = chkMerchantCommonForBusiness(mtp);

		// 检查是否允许本商户充值
		if (!PCPSUtil.compareObject(merchant.getAllowDeposit(),
				Merchant.MERCHANT_ALLOW_DEPOSIT))
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
					"本商户不允许充值交易!");

		// 取商户充值归属销售机构号
		// mtp.depositSaleBranchNo = merchant.getDepositSaleBranchNo();

		// 如果商户未设置,则取发卡商
		// if (PCPSUtil.isEmpty(mtp.depositSaleBranchNo))
		// mtp.depositSaleBranchNo = issueCompany.getDepositSaleBranchNo();

		// if (PCPSUtil.isEmpty(mtp.depositSaleBranchNo))
		// throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
		// "未设置商户充值归属销售机构!");

		// 判断机构号的存在性
		// SaleBranch saleBranch = saleBranchDaoImpl
		// .getSaleBranchBySaleBranchNo(mtp.depositSaleBranchNo);
		// if (saleBranch == null
		// || !mtp.depositSaleBranchNo.startsWith(issueCompany
		// .getIssueCompanyNo()))
		// throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
		// "商户充值归属销售机构设置有误!");

		// 单笔限额判断
		BigInteger eachDepositLimit = merchant.getEachDepositLimit();
		if (eachDepositLimit.compareTo(BigInteger.ZERO) == 0)
			eachDepositLimit = BigInteger.valueOf(issueCompany
					.getMrchEachDepositLimit().intValue());

		int result = eachDepositLimit.compareTo(BigInteger.ZERO);
		if (result < 0)
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
					"发卡商或商户单笔充值额度上限参数设置错!");

		// 计算卡允许的最大可充值额度
		MrchDepositObject mrchDepositObject = new MrchDepositObject();

		mrchDepositObject.cardNo = mtp.depositCard.cardNo;
		mrchDepositObject.issueCompanyNo = mtp.issueCompanyNo;
		mrchDepositObject.depositLimit = BigInteger.ZERO
				.subtract(BigInteger.ONE);

		mrchDepositObject = cardTransBusinessImpl
				.MrchDepositValidate(mrchDepositObject);

		if (result > 0)
			mrchDepositObject.depositLimit = PCPSUtil.setValidateLimit(
					mrchDepositObject.depositLimit, eachDepositLimit);

		// 累计限额判断(等待完善此段代码)

		// 返回充值限额(< 0 不限制)
		mtp.depositCard.depositLimit = mrchDepositObject.depositLimit;

		return mtp;
	}

	/**
	 * 卡充值确认 mtp 商户交易参数对象
	 * 
	 * @throws PCPSApplicationException
	 *             异常信息
	 */
	public MrchTransParam DepositConfirm(MrchTransParam mtp)
			throws PCPSApplicationException {
		// 验证并重设交易时间
		validateTranTime(mtp);

		// 检查充值参数
		checkDepositArgs(mtp);

		// 按照商户端发来的条件检索业务流水
		List<MchTradDetail> mtdlist = mchTradDetailDaoImpl
				.getMchTradDetailByMrchSeqNo(mtp.merchantNo, mtp.tranDate,
						mtp.mrchReqSeqNo);
		// 如果不存在,则返回成功
		if (mtdlist == null || mtdlist.size() != 1)
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"充值确认流水不存在或多于一条!");

		// 取原交易
		MchTradDetail origmtd = mtdlist.get(0);

		// 原交易必须是充值交易
		if (!PCPSUtil.compareObject(MchTradDetail.TRANCODE_DEPOSIT, origmtd
				.getTranCode()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"待核查确认的交易不是充值交易!");

		// 如原交易已经被冲正
		if (!PCPSUtil.isEmpty(origmtd.getCancelTranSeqNo()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"待核查确认的交易已经被冲正!");

		// 如原交易已经做取消处理 -- 返回交易不存在
		if (!PCPSUtil.isEmpty(origmtd.getOtherMrchSeqNo()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"待核查确认的交易已经被撤销!");

		// 检查发生额,冲正时的发生额必须为原充值交易的发生额
		if (!origmtd.getAmount().equals(mtp.depositCard.amount))
			throw new TranApplicationException(TuxedoRespCode.RC_DISCORD,
					"与待核查确认的交易金额不一致," + origmtd.getAmount() + " <> "
							+ mtp.depositCard.amount);

		// 返回主机流水号
		mtp.tranSeqNo = origmtd.getTranSeqNo();

		// 返回卡当时充值后的余额
		List<AccountSystemAddTradDetail> asatd_list = accountSystemAddTradDetailDaoImpl
				.getAllAccountSystemAddTradDetailByAccSeqNo(origmtd
						.getAccAckSeqNo());

		if (asatd_list == null || asatd_list.size() != 1)
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"待核查确认的交易记帐流水号不唯一!");

		// 取充值后余额
		mtp.depositCard.balance = asatd_list.get(asatd_list.size() - 1)
				.getMasBalance();
		mtp.depositCard.fee = origmtd.getFee();

		return mtp;
	}

	/**
	 * 本地充值业务逻辑
	 * 
	 * @param mtp
	 *            商户交易基础信息
	 * @return MerchantPaidCommon
	 */
	private MchTradDetail LocalDeposit(MrchTransParam mtp)
			throws PCPSApplicationException {
		// 商户支付检查
		Merchant merchant = chkMerchantCommonForBusiness(mtp);

		// 获取商户充值的归属销售机构号
		getValidateDepositSaleBranch(mtp, merchant);

		// 商户请求流水号唯一性检查
		checkMrchSeqNo(mtp);

		// 产生交易流水号
		generateMerchantTranSeqNo(merchant, mtp);

		// 记载交易流水

		// 创建充值交易流水一般性信息
		MchTradDetail mtd = createDepositMchTradDetail(mtp);

		// 支付交易特定信息
		mtd.setTranCode(MchTradDetail.TRANCODE_DEPOSIT);
		mtd.setAmount(mtp.depositCard.amount);

		return (mtd);
	}

	/**
	 * 执行卡系统充值
	 * 
	 * @param mtp
	 * @return
	 * @throws PCPSApplicationException
	 */
	private MrchDepositObject CardDeposit(MrchTransParam mtp, MchTradDetail mtd)
			throws PCPSApplicationException {

		// 执行卡充值
		MrchDepositObject depositObject = new MrchDepositObject();

		// 商户号
		depositObject.merchantNo = mtp.merchantNo;

		// 发卡商
		depositObject.issueCompanyNo = mtp.issueCompanyNo;

		// 归属销售机构号
		// depositObject.saleBranchNo = mtp.depositSaleBranchNo;

		// 帐务日期
		depositObject.tranDate = mtp.tranDate;
		depositObject.tranTime = mtp.curSysDateTime;

		// 交易系统流水号 -- 每商户每日唯一
		depositObject.tranSeqNo = mtp.tranSeqNo;

		// 被取消的帐户子系统流水号
		depositObject.cancelAccSeqNo = null;

		// 设置卡记帐信息
		depositObject.cardNo = mtp.depositCard.cardNo;
		depositObject.amount = mtp.depositCard.amount;
		if (PCPSUtil.isEmpty(mtp.mrchAbstract))
			mtp.mrchAbstract = "充值";
		depositObject.remark = mtp.mrchAbstract;

		// 计算充值手续费
		MerchantCardFee mcf = merchantFeePolicyBusinessImpl.calcCardHolderFee(
				mtp.merchantNo, mtp.mrchBrchNo, mtp.tranDate,
				MerchantFeePolicy.TRANTYPE_DEPOSIT, null, depositObject.cardNo,
				depositObject.amount);
		depositObject.fee = mcf.fee;

		// 确认不会越充越少
		if (BigInteger.ZERO.compareTo(mcf.fee) < 0
				&& mcf.fee.compareTo(depositObject.amount) >= 0)
			throw new CardTranApplicationException(TuxedoRespCode.RC_AMOUNT,
					String
							.format("卡%s发生额%s手续费%s无效", depositObject.cardNo,
									depositObject.amount.toString(), mcf.fee
											.toString()));
		// 请求卡系统商户充值记帐
		MrchDepositObject depositResult = cardTransBusinessImpl
				.MrchDeposit(depositObject);

		// 记载卡记帐明细
		MchAccDetail mad = new MchAccDetail();

		mad.setAccSeqNo(depositResult.accSeqNo);
		mad.setAccSubSeqNo(0);
		mad.setMerchantNo(mtd.getMerchantNo());
		mad.setTranDate(mtd.getTranDate());
		mad.setTranSeqNo(mtd.getTranSeqNo());
		mad.setCardNo(mtp.depositCard.cardNo);
		mad.setMasterAccNo(depositResult.masterAccNo);
		mad.setSubAccNo(depositResult.subAccNo);
		mad.setOpenBranchNo(depositResult.saleBranchNo);
		mad.setSalesGroupId(null);
		mad.setPaidAmount(BigInteger.ZERO);
		mad.setDepositAmount(mtp.depositCard.amount);
		mad.setFeeAmount(mcf.fee);

		mchAccDetailDaoImpl.insert(mad);

		// 设置交易流水关于卡系统的记帐流水号
		mtd.setAccAckSeqNo(depositResult.accSeqNo);
		mtd.setFee(mcf.fee);

		// 向client端设置返回的余额列表
		mtp.depositCard.balance = depositResult.afterBalance;
		mtp.depositCard.fee = mcf.fee;

		return (depositResult);
	}

	/**
	 * 卡充值 mtp 商户交易参数对象
	 * 
	 * @throws PCPSApplicationException
	 *             异常信息
	 */
	public MrchTransParam Deposit(MrchTransParam mtp)
			throws PCPSApplicationException {
		// 验证并重设交易时间
		validateTranTime(mtp);

		// 检查充值参数
		checkDepositArgs(mtp);

		// 本地充值业务逻辑
		MchTradDetail mtd = LocalDeposit(mtp);

		// 预付卡充值
		CardDeposit(mtp, mtd);

		// 更新交易明细
		mchTradDetailDaoImpl.insert(mtd);

		return (mtp);
	}

	/**
	 * 充值冲正交易参数检查
	 * 
	 * @param mtp
	 * @throws PCPSApplicationException
	 */
	private void checkDepositCorrArgs(MrchTransParam mtp)
			throws PCPSApplicationException {
		// 商户检查
		if (PCPSUtil.isEmpty(mtp.merchantNo))
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					"商户号为空!");

		// 帐务日期检查
		if (mtp.tranDate == null)
			throw new TranApplicationException(TuxedoRespCode.RC_TRANDATE,
					"帐务日期为空!");

		// 请求流水号检查
		if (PCPSUtil.isEmpty(mtp.mrchReqSeqNo))
			throw new TranApplicationException(TuxedoRespCode.RC_REQSEQNO,
					"请求流水号为空!");

		// 发生额检查
		if (mtp.depositCard == null || mtp.depositCard.amount == null
				|| BigInteger.ZERO.compareTo(mtp.depositCard.amount) >= 0)
			throw new TranApplicationException(TuxedoRespCode.RC_AMOUNT,
					"金额无效 !");
	}

	/**
	 * 本地充值冲正业务逻辑
	 * 
	 * @param mtp
	 *            商户充值冲正基础信息
	 * @return MchTradDetail
	 */
	private MchTradDetail LocalDepositCorr(MrchTransParam mtp,
			MchTradDetail origmtd) throws PCPSApplicationException {
		// 商户支付检查
		Merchant merchant = chkMerchantCommonForBusiness(mtp);

		// 产生交易流水号
		generateMerchantTranSeqNo(merchant, mtp);

		// 置原交易的冲正流水号
		origmtd.setCancelTranSeqNo(mtp.tranSeqNo);

		// 更新原交易流水
		mchTradDetailDaoImpl.update(origmtd);

		// 记载交易流水

		// 创建交易流水一般性信息 -- 根据被冲正交易流水号
		MchTradDetail mtd = createMchTradDetailByOrig(mtp, origmtd);

		mtd.setMrchSeqNo(mtp.mrchReqSeqNo + "_C");

		// 充值交易特定信息
		mtd.setTranCode(MchTradDetail.TRANCODE_DEPOSIT_CORR);
		mtd.setAmount(BigInteger.ZERO.subtract(origmtd.getAmount()));

		// 置原交易流水号
		mtd.setCancelTranSeqNo(origmtd.getTranSeqNo());

		return (mtd);
	}

	/**
	 * 执行卡系统充值取消
	 * 
	 * @param mpc
	 * @return
	 * @throws POSTApplicationException
	 */
	private void CardDepositCorr(MrchTransParam mtp, MchTradDetail origmtd,
			MchTradDetail mtd) throws PCPSApplicationException {

		// 执行卡充值冲正
		MrchDepositObject depositObject = new MrchDepositObject();

		// 发卡商
		depositObject.issueCompanyNo = mtp.issueCompanyNo;

		// 商户号
		depositObject.merchantNo = mtp.merchantNo;

		// 归属销售机构号
		// depositObject.saleBranchNo = mtp.depositSaleBranchNo;

		// 帐务日期
		depositObject.tranDate = mtd.getTranDate();
		depositObject.tranTime = mtp.curSysDateTime;

		// 交易系统流水号 -- 每商户每日唯一
		depositObject.tranSeqNo = mtd.getTranSeqNo();

		// 被取消的帐户子系统流水号
		depositObject.cancelAccSeqNo = origmtd.getAccAckSeqNo();

		// 设置卡记帐信息
		depositObject.amount = origmtd.getAmount();
		if (PCPSUtil.isEmpty(mtp.mrchAbstract))
			mtp.mrchAbstract = "充值冲正/撤销";
		depositObject.remark = mtp.mrchAbstract;

		// 请求卡系统撤销商户充值记帐
		MrchDepositObject depositResult = cardTransBusinessImpl
				.CancelMrchDeposit(depositObject);

		// 设置交易流水关于卡系统的记帐流水号
		mtd.setAccAckSeqNo(depositResult.accSeqNo);

		// 向client端设置返回的余额列表
		mtp.depositCard.balance = depositResult.afterBalance;

		// 记载详细的记帐明细信息
		createMchAccDetail(origmtd, mtd);
	}

	/**
	 * 卡充值冲正 mtp 商户交易参数对象
	 * 
	 * @throws PCPSApplicationException
	 *             异常信息
	 */
	public MrchTransParam DepositCorrect(MrchTransParam mtp)
			throws PCPSApplicationException {
		// 验证并重设交易时间
		validateTranTime(mtp);

		// 充值冲正业务入口参数检查
		checkDepositCorrArgs(mtp);

		// 按照商户端发来的条件检索业务流水
		List<MchTradDetail> mtdlist = mchTradDetailDaoImpl
				.getMchTradDetailByMrchSeqNo(mtp.merchantNo, mtp.tranDate,
						mtp.mrchReqSeqNo);
		// 如果不存在,则返回成功
		if (mtdlist == null || mtdlist.size() < 1)
			return mtp;

		if (mtdlist.size() > 1)
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被冲正流水多于一条!");

		// 取原交易
		MchTradDetail origmtd = mtdlist.get(0);

		// 原交易必须是充值交易
		if (!PCPSUtil.compareObject(MchTradDetail.TRANCODE_DEPOSIT, origmtd
				.getTranCode()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被冲正交易不是充值交易!");

		// 如原交易已经被冲正 -- 返回成功
		if (!PCPSUtil.isEmpty(origmtd.getCancelTranSeqNo()))
			return mtp;

		// 如原交易已经做取消处理 -- 返回交易不存在
		if (!PCPSUtil.isEmpty(origmtd.getOtherMrchSeqNo()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被冲正交易已经被撤销!");

		// 检查发生额,冲正时的发生额必须为原充值交易的发生额
		if (!origmtd.getAmount().equals(mtp.depositCard.amount))
			throw new TranApplicationException(TuxedoRespCode.RC_DISCORD,
					"冲正交易额不一致," + origmtd.getAmount() + " <> "
							+ mtp.depositCard.amount);

		// 本地冲正
		MchTradDetail mtd = LocalDepositCorr(mtp, origmtd);

		// 撤销卡系统记帐
		CardDepositCorr(mtp, origmtd, mtd);

		// 记载冲正交易流水
		mchTradDetailDaoImpl.insert(mtd);

		return mtp;
	}

	/**
	 * 充值撤销交易参数检查
	 * 
	 * @param mtp
	 * @throws PCPSApplicationException
	 */
	private void checkDepositCancelArgs(MrchTransParam mtp)
			throws PCPSApplicationException {
		// 商户检查
		if (PCPSUtil.isEmpty(mtp.merchantNo))
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					"商户号为空!");

		// 帐务日期检查
		if (mtp.tranDate == null)
			throw new TranApplicationException(TuxedoRespCode.RC_TRANDATE,
					"帐务日期为空!");

		// 请求流水号检查
		if (PCPSUtil.isEmpty(mtp.mrchReqSeqNo))
			throw new TranApplicationException(TuxedoRespCode.RC_PARAMETER,
					"请求流水号为空!");

		// 原交易流水号
		if (PCPSUtil.isEmpty(mtp.cancelTranSeqNo))
			throw new TranApplicationException(TuxedoRespCode.RC_PARAMETER,
					"原交易流水号为空!");

		// 原请求流水号
		if (PCPSUtil.isEmpty(mtp.cancelMrchReqSeqNo))
			throw new TranApplicationException(TuxedoRespCode.RC_PARAMETER,
					"原请求流水号为空!");

		// 原交易日期
		if (mtp.cancelTranDate == null)
			throw new TranApplicationException(TuxedoRespCode.RC_PARAMETER,
					"原交易日期为空!");

		// 发生额检查
		if (mtp.totalAmount == null
				|| BigInteger.ZERO.compareTo(mtp.totalAmount) >= 0)
			throw new TranApplicationException(TuxedoRespCode.RC_AMOUNT,
					"金额无效 !");
	}

	/**
	 * 本地充值撤销业务逻辑
	 * 
	 * @param mtp
	 *            商户充值撤销基础信息
	 * @return MchTradDetail
	 */
	private MchTradDetail LocalDepositCancel(MrchTransParam mtp,
			MchTradDetail origmtd) throws PCPSApplicationException {
		// 商户支付检查
		Merchant merchant = chkMerchantCommonForBusiness(mtp);

		// 商户请求流水号唯一性检查
		checkMrchSeqNo(mtp);

		// 产生交易流水号
		generateMerchantTranSeqNo(merchant, mtp);

		// 置原交易的撤销流水号
		origmtd.setOtherTranSeqNo(mtp.tranSeqNo);
		origmtd.setOtherTranDate(mtp.tranDate);
		origmtd.setOtherMrchSeqNo(mtp.mrchReqSeqNo);

		// 更新原交易流水
		mchTradDetailDaoImpl.update(origmtd);

		// 记载交易流水

		// 创建交易流水一般性信息 -- 根据被撤销交易流水号
		MchTradDetail mtd = createMchTradDetailByOrig(mtp, origmtd);

		// 支付交易特定信息
		mtd.setMrchSeqNo(mtp.mrchReqSeqNo);

		// 充值交易特定信息
		mtd.setTranCode(MchTradDetail.TRANCODE_DEPOSIT_CANCEL);
		mtd.setAmount(BigInteger.ZERO.subtract(origmtd.getAmount()));

		// 置原交易流水号
		mtd.setOtherTranSeqNo(origmtd.getTranSeqNo());
		mtd.setOtherTranDate(origmtd.getTranDate());
		mtd.setOtherMrchSeqNo(origmtd.getMrchSeqNo());

		return (mtd);
	}

	/**
	 * 卡充值撤销 mtp 商户交易参数对象
	 * 
	 * @throws PCPSApplicationException
	 *             异常信息
	 */
	public MrchTransParam DepositCancel(MrchTransParam mtp)
			throws PCPSApplicationException {
		boolean ishis = false;

		// 验证并重设交易时间
		validateTranTime(mtp);

		// 产生充值卡对象
		mtp.depositCard = mtp.new CardAccount();

		// 充值撤销冲正业务入口参数检查
		checkDepositCancelArgs(mtp);

		// 按照商户端发来的条件检索业务流水
		List<MchTradDetail> mtdlist = mchTradDetailDaoImpl
				.getMchTradDetailByMrchSeqNo(mtp.merchantNo,
						mtp.cancelTranDate, mtp.cancelMrchReqSeqNo);
		// 如果不存在,则从历史表查找,如仍不存在,则抛出异常
		if (mtdlist == null || mtdlist.size() == 0) {
			mtdlist = mchTradDetailDaoImpl
					.getMchTradDetailByMrchSeqNoFromHistory(mtp.merchantNo,
							mtp.cancelTranDate, mtp.cancelMrchReqSeqNo);
			if (mtdlist == null || mtdlist.size() != 1)
				throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
						"被撤销流水不存在或者多于一条!");
			ishis = true;
		}

		if (mtdlist.size() != 1)
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被撤销流水不存在或多于一条!");

		// 取原交易
		MchTradDetail origmtd = mtdlist.get(0);

		// 原交易必须是充值交易
		if (!PCPSUtil.compareObject(MchTradDetail.TRANCODE_DEPOSIT, origmtd
				.getTranCode()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被撤销交易不是充值交易!");

		// 流水号必须一致
		if (!PCPSUtil
				.compareObject(origmtd.getTranSeqNo(), mtp.cancelTranSeqNo))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被撤销交易的主机流水号不一致," + origmtd.getTranSeqNo() + " <> "
							+ mtp.cancelTranSeqNo);

		// 如原交易已经被冲正
		if (!PCPSUtil.isEmpty(origmtd.getCancelTranSeqNo()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被撤销交易已经被冲正!");

		// 如原交易已经做取消处理 -- 返回交易不存在
		if (!PCPSUtil.isEmpty(origmtd.getOtherMrchSeqNo()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被撤销交易已经被撤销!");

		// 检查发生额,撤销时的发生额必须为原充值交易的发生额
		if (!origmtd.getAmount().equals(mtp.totalAmount))
			throw new TranApplicationException(TuxedoRespCode.RC_DISCORD,
					"撤销交易额不一致," + origmtd.getAmount() + " <> "
							+ mtp.totalAmount);

		if (ishis) {
			// 将该流水从历史表中删除
			PCPSUtil.Log("migrateMchTradDetailFromHistory");
			mchTradDetailDaoImpl.migrateMchTradDetailFromHistory(origmtd
					.getMerchantNo(), origmtd.getTranDate(), origmtd
					.getTranSeqNo());
			PCPSUtil.Log("BEGIN getMchTradDetailByMrchSeqNo");
			mtdlist = mchTradDetailDaoImpl.getMchTradDetailByMrchSeqNo(
					mtp.merchantNo, mtp.cancelTranDate, mtp.cancelMrchReqSeqNo);
			if (mtdlist == null || mtdlist.size() != 1)
				throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
						"迁移历史流水记录到当日错误!");
			origmtd = mtdlist.get(0);
			PCPSUtil.Log("getMchTradDetailByMrchSeqNo OK!!!");
		}

		// 本地撤销
		MchTradDetail mtd = LocalDepositCancel(mtp, origmtd);

		// 撤销卡系统记帐
		CardDepositCorr(mtp, origmtd, mtd);

		// 记载冲正交易流水
		mchTradDetailDaoImpl.insert(mtd);

		return mtp;
	}

	/**
	 * 本地充值撤销冲正业务逻辑
	 * 
	 * @param mtp
	 *            商户充值撤销冲正基础信息
	 * @return MchTradDetail
	 */
	private MchTradDetail LocalDepositCancelCorr(MrchTransParam mtp,
			MchTradDetail origmtd) throws PCPSApplicationException {
		// 商户支付检查
		Merchant merchant = chkMerchantCommonForBusiness(mtp);

		// 产生交易流水号
		generateMerchantTranSeqNo(merchant, mtp);

		// 置原交易的撤销流水号
		origmtd.setCancelTranSeqNo(mtp.tranSeqNo);

		// 更新原交易流水
		mchTradDetailDaoImpl.update(origmtd);

		// 记载交易流水

		// 创建交易流水一般性信息 -- 根据被撤销交易流水号
		MchTradDetail mtd = createMchTradDetailByOrig(mtp, origmtd);

		// 支付交易特定信息
		mtd.setMrchSeqNo(mtp.mrchReqSeqNo + "_C");

		// 充值交易特定信息
		mtd.setTranCode(MchTradDetail.TRANCODE_DEPOSIT_CANCEL_CORR);
		mtd.setAmount(BigInteger.ZERO.subtract(origmtd.getAmount()));

		// 置原交易流水号
		mtd.setCancelTranSeqNo(origmtd.getTranSeqNo());

		// 重置最初的充值交易为未被撤销状态
		restoreOrigMchTradDetail(origmtd);

		return (mtd);
	}

	/**
	 * 执行卡系统重做充值记帐
	 * 
	 * @param mtp
	 * @return
	 * @throws PCPSApplicationException
	 */
	private void redoCardDeposit(MrchTransParam mtp, MchTradDetail origmtd,
			MchTradDetail mtd) throws PCPSApplicationException {

		// 执行卡充值冲正
		MrchDepositObject depositObject = new MrchDepositObject();

		// 发卡商
		depositObject.issueCompanyNo = mtp.issueCompanyNo;

		// 商户号
		depositObject.merchantNo = mtp.merchantNo;

		// 归属销售机构号
		// depositObject.saleBranchNo = mtp.depositSaleBranchNo;

		// 帐务日期
		depositObject.tranDate = mtd.getTranDate();
		depositObject.tranTime = mtp.curSysDateTime;

		// 交易系统流水号 -- 每商户每日唯一
		depositObject.tranSeqNo = mtd.getTranSeqNo();

		// 被取消的帐户子系统流水号
		depositObject.redoAccSeqNo = origmtd.getAccAckSeqNo();

		// 设置卡记帐信息
		depositObject.amount = origmtd.getAmount();
		if (PCPSUtil.isEmpty(mtp.mrchAbstract))
			mtp.mrchAbstract = "重做充值";
		depositObject.remark = mtp.mrchAbstract;

		// 请求卡系统撤销商户充值记帐
		MrchDepositObject depositResult = cardTransBusinessImpl
				.redoMrchDeposit(depositObject);

		// 设置交易流水关于卡系统的记帐流水号
		mtd.setAccAckSeqNo(depositResult.accSeqNo);

		// 向client端设置返回的余额列表
		mtp.depositCard.balance = depositResult.afterBalance;

		// 记载详细的记帐明细信息
		createMchAccDetail(origmtd, mtd);
	}

	/**
	 * 卡充值撤销冲正 mtp 商户交易参数对象
	 * 
	 * @throws PCPSApplicationException
	 *             异常信息
	 */
	public MrchTransParam DepositCancelCorrect(MrchTransParam mtp)
			throws PCPSApplicationException {
		// 验证并重设交易时间
		validateTranTime(mtp);

		// 充值冲正业务入口参数检查
		checkDepositCancelArgs(mtp);

		// 按照商户端发来的条件检索业务流水
		List<MchTradDetail> mtdlist = mchTradDetailDaoImpl
				.getMchTradDetailByMrchSeqNo(mtp.merchantNo, mtp.tranDate,
						mtp.mrchReqSeqNo);
		// 如果不存在,则返回成功 -- 再查询历史,如仍不存在,则抛出异常
		if (mtdlist == null || mtdlist.size() < 1)
			return mtp;

		if (mtdlist.size() > 1)
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被冲正流水多于一条!");

		// 取原交易
		MchTradDetail origmtd = mtdlist.get(0);

		// 原交易必须是充值撤销交易
		if (!PCPSUtil.compareObject(MchTradDetail.TRANCODE_DEPOSIT_CANCEL,
				origmtd.getTranCode()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"被冲正交易不是充值撤销交易!");

		// 如原交易已经被冲正
		if (!PCPSUtil.isEmpty(origmtd.getCancelTranSeqNo()))
			return mtp;

		// 检查发生额,冲正时的发生额必须为原充值撤销交易发生额的相反数
		if (!origmtd.getAmount().add(mtp.totalAmount).equals(BigInteger.ZERO))
			throw new TranApplicationException(TuxedoRespCode.RC_DISCORD,
					"冲正交易额不一致," + origmtd.getAmount() + " <> "
							+ mtp.totalAmount);

		// 本地充值撤销冲正
		MchTradDetail mtd = LocalDepositCancelCorr(mtp, origmtd);

		// 卡系统重新记帐
		redoCardDeposit(mtp, origmtd, mtd);

		// 记载冲正交易流水
		mchTradDetailDaoImpl.insert(mtd);

		return mtp;
	}

	/**
	 * 对帐交易参数检查
	 * 
	 * @param mtp
	 * @throws PCPSApplicationException
	 */
	private void checkAccountArgs(MrchTransParam mtp)
			throws PCPSApplicationException {
		// 金额检查
		if (BigInteger.ZERO.compareTo(mtp.debitCount) > 0
				|| BigInteger.ZERO.compareTo(mtp.debitAmount) > 0
				|| BigInteger.ZERO.compareTo(mtp.creditCount) > 0
				|| BigInteger.ZERO.compareTo(mtp.creditAmount) > 0)
			throw new TranApplicationException(TuxedoRespCode.RC_PARAMETER,
					"对帐笔数或金额非法!");

		// 商户检查
		if (PCPSUtil.isEmpty(mtp.merchantNo))
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					"商户号为空!");

		// 帐务日期检查
		if (mtp.tranDate == null)
			throw new TranApplicationException(TuxedoRespCode.RC_TRANDATE,
					"帐务日期为空!");

		// 检索商户信息
		Merchant merchant = merchantDaoImpl
				.getMerchantForUpdate(mtp.merchantNo);
		if (merchant == null)
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					"商户号" + mtp.merchantNo + "不存在!");

		// 判断是否激活
		if (!merchant.getActivate().equals(Merchant.MERCHANT_ACTIVATE))
			throw new TranApplicationException(TuxedoRespCode.RC_NOMERCHANT,
					"商户尚未激活!");

		int result = mtp.tranDate.compareTo(merchant.getTranDate());

		PCPSUtil.Log(mtp.tranDate.toString());

		// 当前的日期不能>Merchant.TranDate
		if (result > 0)
			throw new TranApplicationException(TuxedoRespCode.RC_DISTRAN,
					"日期不能比商户的当前帐务日期晚!");

		// 与当前交易日期一致,继续
		if (result <= 0) {
			// PCPSUtil.Log(String.format("switch merchant(%s) trandate to
			// (%s)!",
			// merchant.getMerchantNo(), PCPSUtil
			// .getPackedYMDString(tranDate)));
		}
	}

	/**
	 * PCPS 对帐业务接口
	 * 
	 * @param chkAccFile
	 *            对帐信息
	 * @return void
	 */
	public void CheckAccInf(ChkAccFile chkAccFile, ChkAccFile userChkAccFile)
			throws PCPSApplicationException {
		// 核对笔数
		if (!PCPSUtil.compareObject(userChkAccFile.getDebitCount(), chkAccFile
				.getDebitCount())
				|| !PCPSUtil.compareObject(userChkAccFile.getCreditCount(),
						chkAccFile.getCreditCount()))
			throw new PCPSApplicationException(
					TuxedoRespCode.RC_ACCCHKFILE,
					String
							.format(
									"TRAN CHK ACC COUNT CONFLICT DEBITCOUNT[%s/%s] CREDITCOUNT[%s/%s]",
									userChkAccFile.getDebitCount().toString(),
									chkAccFile.getDebitCount().toString(),
									userChkAccFile.getCreditCount().toString(),
									chkAccFile.getCreditCount().toString()));

		// 核对金额
		if (!PCPSUtil.compareObject(userChkAccFile.getDebitAmount(), chkAccFile
				.getDebitAmount())
				|| !PCPSUtil.compareObject(userChkAccFile.getCreditAmount(),
						chkAccFile.getCreditAmount()))
			throw new PCPSApplicationException(
					TuxedoRespCode.RC_ACCCHKFILE,
					String
							.format(
									"TRAN CHK ACC AMOUNT CONFLICT DEBITAMOUNT[%s/%s] CREDITAMOUNT[%s/%s]",
									userChkAccFile.getDebitAmount().toString(),
									chkAccFile.getDebitAmount().toString(),
									userChkAccFile.getCreditAmount().toString(),
									chkAccFile.getCreditAmount().toString()));
	}

	/**
	 * PMBS 对帐文件进入数据库
	 * 
	 * @param chkAccFile
	 *            对帐信息
	 * @return void
	 */
	private void CheckAccFileEnter(ChkAccFile chkAccFile)
			throws PCPSApplicationException {

		boolean isnew = false;

		// 检索对帐控制
		MchChkAccCtl mcac = mchChkAccCtlDaoImpl.getMchChkAccCtlByKey(chkAccFile
				.getMerchantNo(), chkAccFile.getTranDate());

		// 如果记录存在且对帐文件存在,则删除已存在的对帐文件
		if (mcac != null && mcac.getCheckFileId() != null) {
			binaryFileInfoDaoImpl.deleteBinaryFileInfoById(mcac
					.getCheckFileId());
		}

		isnew = (mcac == null);

		if (isnew)
			mcac = new MchChkAccCtl();

		// 删除对帐明细
		mchChkAccDetailDaoImpl.delAllMchChkAccDetailDaoByCheckDate(chkAccFile
				.getMerchantNo(), chkAccFile.getTranDate());

		// 重新形成对帐控制
		mcac.setMerchantNo(chkAccFile.getMerchantNo());
		mcac.setCheckDate(chkAccFile.getTranDate());
		mcac.setCheckResult(MchChkAccCtl.CHECKRESULT_SUCC);
		mcac.setDebitCount(chkAccFile.getDebitCount());
		mcac.setDebitAmt(chkAccFile.getDebitAmount());
		mcac.setCreditCount(chkAccFile.getCreditCount());
		mcac.setCreditAmt(chkAccFile.getCreditAmount());

		BigInteger[] all = mchTradDetailDaoImpl.checkAccountSummary(chkAccFile
				.getMerchantNo(), chkAccFile.getTranDate());

		// 核对笔数及金额
		if (!PCPSUtil.compareObject(all[0], chkAccFile.getDebitCount())
				|| !PCPSUtil.compareObject(all[2], chkAccFile.getCreditCount())
				|| !PCPSUtil.compareObject(all[1], chkAccFile.getDebitAmount())
				|| !PCPSUtil
						.compareObject(all[3], chkAccFile.getCreditAmount()))
			mcac.setCheckResult(MchChkAccCtl.CHECKRESULT_MANUAL);

		BinaryFileInfo bfi = new BinaryFileInfo();
		BigInteger filecontentid = binaryFileInfoDaoImpl
				.getUniqueBinaryFileKey();

		bfi.setFileId(filecontentid);
		bfi.setFileContent(chkAccFile.getFileContent());
		bfi.setFileType(BinaryFileInfo.XML);
		bfi.setFileLength(chkAccFile.getFileContent().length);

		// 插入文件
		binaryFileInfoDaoImpl.insertBinaryFileInfo(bfi);

		mcac.setCheckFileId(filecontentid);

		if (isnew)
			mchChkAccCtlDaoImpl.insert(mcac);
		else
			mchChkAccCtlDaoImpl.update(mcac);
	}

	/**
	 * 商户对帐
	 * 
	 * @throws PCPSApplicationException
	 */
	public MrchTransParam CheckAccount(MrchTransParam mtp)
			throws PCPSApplicationException {
		// 验证并重设交易时间
		validateTranTime(mtp);

		// 交易检查
		checkAccountArgs(mtp);

		// 取商户
		Merchant merchant = merchantDaoImpl
				.getMerchantByMerchantNo(mtp.merchantNo);

		// 取发卡商
		IssueCompany issueCompany = issueCompanyDaoImpl
				.getIssueCompanyByIssueCompanyNo(merchant.getIssueCompanyNo());

		// 必须是允许对帐的商户才能发起对帐交易
		if (!PCPSUtil.compareObject(merchant.getCheckAccMode(),
				Merchant.CHECKACCMODE_MERCHANT))
			throw new PCPSApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
					"该商户不允许发起对帐交易!");

		if (PCPSUtil.isEmpty(issueCompany.getChkAccFtpAddr()))
			throw new PCPSApplicationException(TuxedoRespCode.RC_ACCCHKFILE,
					"发卡商未设置对帐文件FTP地址参数!");

		// 取对帐文件
		ChkAccFile userChkAccFile = chkAccFileBusinessImpl.getChkAccFile(
				issueCompany.getChkAccFtpAddr(), mtp.merchantNo, mtp.tranDate);
		if (userChkAccFile == null || userChkAccFile.getFileContent() == null
				|| userChkAccFile.getFileDetail() == null)
			throw new PCPSApplicationException(TuxedoRespCode.RC_ACCCHKFILE,
					"对帐文件不存在或格式非法!");

		// 检查记录数是否一致
		BigInteger allcnt = mtp.debitCount.add(mtp.creditCount);
		if (userChkAccFile.getFileDetail().size() != allcnt.intValue())
			throw new PCPSApplicationException(TuxedoRespCode.RC_ACCCHKFILE,
					"对帐文件明细记录数与对帐请求业务总笔数不一致!");

		ChkAccFile sum = userChkAccFile.getSummaryFromDetail();
		if (!ChkAccFile.isEqual(sum, userChkAccFile))
			throw new PCPSApplicationException(TuxedoRespCode.RC_ACCCHKFILE,
					"对帐文件明细与汇总不一致!");

		// 产生对象,并且赋值
		ChkAccFile chkAccFile = new ChkAccFile();

		chkAccFile.setDebitCount(mtp.debitCount);
		chkAccFile.setDebitAmount(mtp.debitAmount);
		chkAccFile.setCreditCount(mtp.creditCount);
		chkAccFile.setCreditAmount(mtp.creditAmount);

		// 核对文件与交易中汇总信息的一致性,确保发来的对帐文件与对帐请求中的数字一致
		CheckAccInf(chkAccFile, userChkAccFile);

		// 对帐文件进入数据库
		CheckAccFileEnter(userChkAccFile);

		// 从ftp地址处删除文件(15天以前的文件)
		java.util.Calendar cal = java.util.Calendar.getInstance();
		cal.setTime(mtp.tranDate);
		cal.add(java.util.Calendar.DAY_OF_MONTH, -15);
		java.util.Date olddate = cal.getTime();

		chkAccFileBusinessImpl.delRemoteFile(issueCompany.getChkAccFtpAddr(),
				mtp.merchantNo, olddate);

		return mtp;
	}

	/**
	 * 手工调帐
	 * 
	 * @param adjustMerchantNo
	 *            待调帐商户号
	 * @param adjustTranSeqNo
	 *            待调帐流水号
	 * @param adjustTranDate
	 *            待调帐日期
	 * @param amount
	 *            原交易发生金额
	 * @param operId
	 *            调帐人ID
	 * @param remark
	 *            调帐备注
	 * @throws PCPSApplicationException
	 */
	public void AdjustAccount(String adjustMerchantNo, String adjustTranSeqNo,
			Date adjustTranDate, BigInteger amount, BigInteger operId,
			String remark) throws PCPSApplicationException {
		// 入口参数检查
		if (PCPSUtil.isEmpty(adjustMerchantNo)
				|| PCPSUtil.isEmpty(adjustTranSeqNo)
				|| (adjustTranDate == null) || (amount == null)
				|| BigInteger.ZERO.compareTo(amount) > 0 || (operId == null)
				|| PCPSUtil.isEmpty(remark))
			throw new PCPSApplicationException("", "非法的调帐参数!");

		// 取SysOpUser
		SysOpUser oper = sysOpUserDaoImpl.getSysOpUserByOperId(operId);
		if (oper == null)
			throw new PCPSApplicationException("", "无效的操作员ID!");

		// 取IssueCompany
		IssueCompany issueCompany = issueCompanyDaoImpl
				.getIssueCompanyByIssueCompanyNo(PCPSUtil
						.getICNoByBranchNo(oper.getOperBranchNo()));
		if (issueCompany == null)
			throw new PCPSApplicationException("", "无效的发卡商!");

		// 取商户号
		Merchant merchant = merchantDaoImpl
				.getMerchantByMerchantNo(adjustMerchantNo);
		if (merchant == null)
			throw new PCPSApplicationException("", String.format("无效的商户号%s!",
					adjustMerchantNo));

		// 商户必须归属于该发卡商
		if (!PCPSUtil.compareObject(merchant.getIssueCompanyNo(), issueCompany
				.getIssueCompanyNo()))
			throw new PCPSApplicationException("", "无此商户!");

		// 日期必须一致
		if (!PCPSUtil.compareObject(merchant.getTranDate(), issueCompany
				.getTranDate()))
			throw new PCPSApplicationException("", "商户当前帐务日期与发卡商不一致!");

		// 按照商户端发来的条件检索业务流水
		boolean ishis = false;

		MchTradDetail mtdqry = new MchTradDetail();
		mtdqry.setMerchantNo(adjustMerchantNo);
		mtdqry.setTranDate(adjustTranDate);
		mtdqry.setTranSeqNo(adjustTranSeqNo);
		MchTradDetail origmtd = mchTradDetailDaoImpl
				.getMchTradDetailByPrimaryKey(mtdqry);
		// 如果不存在,则从历史表查找,如仍不存在,则抛出异常
		if (origmtd == null) {
			List<MchTradDetail> mtdlist = mchTradDetailDaoImpl
					.getMchTradDetailByTranSeqNoFromHistory(adjustMerchantNo,
							adjustTranDate, adjustTranSeqNo);
			if (mtdlist == null || mtdlist.size() != 1)
				throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
						"被调帐流水不存在或者多于一条!");
			ishis = true;
			origmtd = mtdlist.get(0);
		}

		// 目前仅支持消费交易的调帐,其他交易后续支持.2013.05.04
		if (!PCPSUtil.compareObject(origmtd.getTranCode(),
				MchTradDetail.TRANCODE_PAID))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"目前仅支持消费交易的调帐!");

		// 如原交易已经被冲正
		if (!PCPSUtil.isEmpty(origmtd.getCancelTranSeqNo()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"交易已被冲正!");

		// 如原交易已经做撤销/退货处理 -- 返回交易不存在
		if (!PCPSUtil.isEmpty(origmtd.getOtherMrchSeqNo()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"交易已被撤销或者退货!");

		// 如原交易已经被调整过 -- 返回交易不存在
		if (!PCPSUtil.isEmpty(origmtd.getAdjustTranSeqNo()))
			throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
					"交易已被调帐处理!");

		// 检查发生额,撤销时的发生额合计必须为原交易的预付卡支付额+会员卡支付额
		BigInteger totalAmount = origmtd.getAmount().add(
				origmtd.getScorePaidAmount()).abs();
		if (!totalAmount.equals(amount))
			throw new TranApplicationException(TuxedoRespCode.RC_DISCORD,
					"被调帐交易合计发生额不一致," + totalAmount.toString() + " <> "
							+ amount.toString());

		if (ishis) {
			// 将该流水从历史表移动到当日表中
			PCPSUtil.Log("migrateMchTradDetailFromHistory");
			mchTradDetailDaoImpl.migrateMchTradDetailFromHistory(origmtd
					.getMerchantNo(), origmtd.getTranDate(), origmtd
					.getTranSeqNo());
			PCPSUtil.Log("BEGIN getMchTradDetailByMrchSeqNo");
			origmtd = mchTradDetailDaoImpl.getMchTradDetailByPrimaryKey(mtdqry);
			if (origmtd == null)
				throw new TranApplicationException(TuxedoRespCode.RC_NONEXIST,
						"迁移历史流水记录到当日错误!");
			PCPSUtil.Log("getMchTradDetailByMrchSeqNo OK!!!");
		}

		MrchTransParam mtp = new MrchTransParam();
		mtp.issueCompanyNo = issueCompany.getIssueCompanyNo();
		mtp.curSysDateTime = pcpsCommonDaoImpl.getDatabaseSysTime();
		mtp.merchantNo = adjustMerchantNo;
		mtp.tranDate = merchant.getTranDate();

		// 其他辅助信息 -- 终端 &机构
		mtp.mrchPosNo = origmtd.getTerminalNo();
		mtp.mrchPosBatchNo = origmtd.getTermSettBatchNo();
		mtp.mrchPosSeqNo = origmtd.getTermSeqNo();
		mtp.mrchTellerNo = origmtd.getMrchTellerNo();
		mtp.mrchBrchNo = origmtd.getMrchBrchNo();

		// 调帐备注
		String adjustRemark = remark;

		// 商户业务摘要
		mtp.mrchAbstract = adjustRemark;

		// 产生交易流水号
		generateMerchantTranSeqNo(merchant, mtp);
		mtp.mrchReqSeqNo = mtp.tranSeqNo + "_ADJUST";

		// 置原交易的调帐流水号
		origmtd.setAdjustTranSeqNo(mtp.tranSeqNo);
		origmtd.setAdjustTranDate(mtp.tranDate);
		origmtd.setAdjustOperId(operId);
		origmtd.setAdjustReamrk(adjustRemark);

		// 更新原交易流水
		mchTradDetailDaoImpl.update(origmtd);

		// 记载交易流水

		// 创建交易流水一般性信息 -- 根据被撤销交易流水号
		MchTradDetail mtd = createMchTradDetailByOrig(mtp, origmtd);

		// 交易特定信息
		// 交易码与原交易相同
		mtd.setTranCode(origmtd.getTranCode());
		// 金额变反
		mtd.setAmount(BigInteger.ZERO.subtract(origmtd.getAmount()));
		mtd.setFee(BigInteger.ZERO.subtract(origmtd.getFee()));
		mtd.setScorePaidAmount(BigInteger.ZERO.subtract(origmtd
				.getScorePaidAmount()));

		// 新交易置原交易流水号
		mtd.setAdjustTranSeqNo(adjustTranSeqNo);
		mtd.setAdjustTranDate(adjustTranDate);
		mtd.setAdjustOperId(operId);
		mtd.setAdjustReamrk(adjustRemark);

		// 记载附加产品信息 -- 根据原交易
		createMchAddTradDetailByOrig(mtp, origmtd);

		// 记载支付附加信息 -- 根据原交易
		createMchAddTradSummaryByOrig(mtp, origmtd);

		// 创建附加卡详细信息 -- 根据原交易
		createMchAddCardDetailByOrig(mtp, origmtd);

		// 执行卡支付取消,如果原交易产生了积分,则同时冲正积分的产生
		if (!PCPSUtil.isEmpty(origmtd.getAccAckSeqNo()))
			CardPaidCancel(mtp, origmtd, mtd);

		// 记载冲正交易流水
		mchTradDetailDaoImpl.insert(mtd);
	}

	/**
	 * 检查商户差错处理参数的合法性
	 * 
	 * @param mmObj
	 * @throws PCPSApplicationException
	 */
	private Object[] checkMchMistakeObject(MchMistakeObject mmObj)
			throws PCPSApplicationException {
		if (mmObj == null)
			throw new PCPSApplicationException("", "对象为空!");

		if (PCPSUtil.isEmpty(mmObj.merchantNo)
				|| PCPSUtil.isEmpty(mmObj.merchantBranchNo))
			throw new PCPSApplicationException("", "商户号或商户机构号为空!");

		Object[] obj = new Object[3];

		Merchant merchant = merchantDaoImpl
				.getMerchantByMerchantNo(mmObj.merchantNo);
		if (merchant == null)
			throw new PCPSApplicationException("", "商户号非法!");

		obj[0] = merchant;

		MerchantBranch mb = merchantBranchDaoImpl
				.getMerchantBranchByMerchantBranchNo(mmObj.merchantBranchNo);
		if (mb == null
				|| !PCPSUtil.compareObject(mb.getMerchantNo(), merchant
						.getMerchantNo()))
			throw new PCPSApplicationException("", "商户机构号非法!");

		if (PCPSUtil.isEmpty(mmObj.cardNo))
			throw new PCPSApplicationException("", "卡号为空!");

		ActivateCard ac = activateCardDaoImpl
				.getActivateCardByCardNo(mmObj.cardNo);
		if (ac == null)
			throw new PCPSApplicationException("", "卡号非法!");

		obj[1] = ac;

		MakeCardControl mcc = makeCardControlDaoImpl
				.getMakeCardControlByBatchNo(ac.getBatchNo());
		if (mcc == null)
			throw new PCPSApplicationException("", "卡号制卡控制信息不存在!");

		if (PCPSUtil.isEmpty(mmObj.tranType))
			throw new PCPSApplicationException("", "交易类型为空!");

		if (!PCPSUtil.compareObject(mmObj.tranType,
				MchMistakeObject.TRANTYPE_CONSUME)
				&& !PCPSUtil.compareObject(mmObj.tranType,
						MchMistakeObject.TRANTYPE_DEPOSIT))
			throw new PCPSApplicationException("", "交易类型非法[" + mmObj.tranType
					+ "]!");

		if (PCPSUtil.compareObject(mmObj.tranType,
				MchMistakeObject.TRANTYPE_CONSUME)) {
			if (PCPSUtil.compareObject(merchant.getNeedBusinessType(),
					Merchant.BUSINESSTYPE_NEED)) {
				if (PCPSUtil.isEmpty(mmObj.businessType))
					throw new PCPSApplicationException("",
							"必须输入商户消费业务/商品/服务类型!");
				MerchantBusinessType mbt = new MerchantBusinessType();
				mbt.setMerchantNo(mmObj.merchantNo);
				mbt.setBusinessType(mmObj.businessType);
				mbt = merchantBusinessTypeDaoImpl
						.getMerchantBusinessTypeByKey(mbt);
				if (mbt == null)
					throw new PCPSApplicationException("",
							"输入的商户消费业务/商品/服务类型无效!");
			} else {
				if (!PCPSUtil.isEmpty(mmObj.businessType))
					throw new PCPSApplicationException("",
							"无须输入商户消费业务/商品/服务类型!");
			}
		} else
			mmObj.businessType = null;

		if (mmObj.amount == null || mmObj.fee == null)
			throw new PCPSApplicationException("", "金额或手续费为空!");

		if (BigInteger.ZERO.equals(mmObj.amount)
				&& BigInteger.ZERO.equals(mmObj.fee))
			throw new PCPSApplicationException("", "金额和手续费不能同时为零!");

		if (mmObj.operId == null)
			throw new PCPSApplicationException("", "操作员号为空!");

		SysOpUser user = sysOpUserDaoImpl.getSysOpUserByOperId(mmObj.operId);
		if (user == null)
			throw new PCPSApplicationException("", "操作员号非法!");

		if (PCPSUtil.isEmpty(mmObj.remark))
			throw new PCPSApplicationException("", "备注为空!");

		IssueCompany issueCompany = issueCompanyDaoImpl
				.getIssueCompanyByIssueCompanyNo(PCPSUtil
						.getICNoByBranchNo(user.getOperBranchNo()));
		if (issueCompany == null)
			throw new PCPSApplicationException("", "发卡商不存在!");

		// 商户、卡必须属于该发卡商
		if (!PCPSUtil.compareObject(merchant.getIssueCompanyNo(), issueCompany
				.getIssueCompanyNo())
				|| !PCPSUtil.compareObject(mcc.getIssueCompanyNo(),
						issueCompany.getIssueCompanyNo()))
			throw new PCPSApplicationException("", "商户、卡及操作员必须为同一发卡商!");

		if (!PCPSUtil.compareObject(ac.getCloseStatus(),
				ActivateCard.ACTIVATECARD_CLOSESTATUS_NORMAL))
			throw new PCPSApplicationException("", "该卡已经注销!");

		obj[2] = issueCompany;

		return obj;
	}

	/**
	 * 分配记帐参数
	 * 
	 * @throws PCPSApplicationException
	 */
	private void distributeMistake(Map<BigInteger, BigInteger> submap,
			List<SubAccount> subacclist,
			ArrayList<AccountParam> accountParamList, BigInteger amt,
			boolean isdebit, String tranType, String summary)
			throws PCPSApplicationException {

		// 如果记帐金额为0,则返回
		if (BigInteger.ZERO.equals(amt))
			return;

		BigInteger newbal = BigInteger.ZERO;

		// 如果借方负值或贷方正值 -- 导致记帐后余额肯定为非负
		if ((isdebit && BigInteger.ZERO.compareTo(amt) > 0)
				|| (!isdebit && BigInteger.ZERO.compareTo(amt) < 0)) {
			// 始终取第1个子帐户进行记帐
			SubAccount sa = subacclist.get(0);
			AccountParam ap = new AccountParam();
			ap.subAccNo = sa.getSubAccNo();
			ap.debitAmount = isdebit ? amt : BigInteger.ZERO;
			ap.creditAmount = isdebit ? BigInteger.ZERO : amt;
			ap.tranType = tranType;
			ap.summary = summary;
			accountParamList.add(ap);
			newbal = submap.remove(sa.getSubAccNo());
			newbal = newbal.add(ap.creditAmount).subtract(ap.debitAmount);
			submap.put(sa.getSubAccNo(), newbal);
			return;
		}

		// 借方正值或贷方负值
		BigInteger newamt = amt.abs();
		for (SubAccount sa : subacclist) {
			newbal = submap.get(sa.getSubAccNo());
			// 始终取第1个非零值
			if (BigInteger.ZERO.equals(newbal))
				continue;
			BigInteger accamt = newamt;
			if (newbal.compareTo(accamt) <= 0)
				accamt = newbal;
			AccountParam ap = new AccountParam();
			ap.subAccNo = sa.getSubAccNo();
			ap.debitAmount = isdebit ? accamt : BigInteger.ZERO;
			ap.creditAmount = isdebit ? BigInteger.ZERO : BigInteger.ZERO
					.subtract(accamt);
			ap.tranType = tranType;
			ap.summary = summary;
			accountParamList.add(ap);
			newbal = submap.remove(sa.getSubAccNo());
			newbal = newbal.add(ap.creditAmount).subtract(ap.debitAmount);
			submap.put(sa.getSubAccNo(), newbal);
			newamt = newamt.subtract(accamt);
			int result = newamt.compareTo(BigInteger.ZERO);
			if (result == 0)
				break;
			if (result < 0)
				throw new PCPSApplicationException("分配记帐金额失败!");
		}
	}

	private void genMistakeAccDetail(ArrayList<AccountParam> accountParamList,
			int amttype, Integer seqno, List<MchAccDetail> madlist) {
		for (AccountParam ap : accountParamList) {
			MchAccDetail mad = new MchAccDetail();
			mad.setAccSubSeqNo(seqno);
			mad.setSubAccNo(ap.subAccNo);
			mad.setPaidAmount(BigInteger.ZERO);
			mad.setDepositAmount(BigInteger.ZERO);
			mad.setFeeAmount(BigInteger.ZERO);
			if (amttype == 0)
				mad.setPaidAmount(ap.debitAmount);
			else if (amttype == 1)
				mad.setDepositAmount(ap.creditAmount);
			else
				mad.setFeeAmount(ap.debitAmount);
			madlist.add(mad);
			seqno++;
		}
	}

	/**
	 * 商户交易差错处理
	 * 
	 * @param mmObj
	 * @return
	 * @throws PCPSApplicationException
	 */
	public MchMistakeObject MistakeAccount(MchMistakeObject mmObj)
			throws PCPSApplicationException {
		// 检查参数的合法性
		Object[] obj = checkMchMistakeObject(mmObj);
		// Merchant merchant = (Merchant) obj[0];
		ActivateCard ac = (ActivateCard) obj[1];
		IssueCompany issueCompany = (IssueCompany) obj[2];

		// 设置返回值
		mmObj.befBalance = ac.getBalance();
		mmObj.tranDate = issueCompany.getTranDate();

		// 借贷记帐(消费和fee始终为借方记帐,充值始终为贷方记帐)
		BigInteger debitAmt = BigInteger.ZERO;
		BigInteger creditAmt = BigInteger.ZERO;
		String tranCode = MchTradDetail.TRANCODE_PAID;
		if (PCPSUtil.compareObject(mmObj.tranType,
				MchMistakeObject.TRANTYPE_DEPOSIT)) {
			creditAmt = mmObj.amount;
			tranCode = MchTradDetail.TRANCODE_DEPOSIT;
		} else
			debitAmt = mmObj.amount;

		// 检查有效余额
		MasterAccount mas = masterAccountDaoImpl
				.getMasterAccountByMasterAccNo(ac.getMasterAccNo());
		if (mas == null)
			throw new PCPSApplicationException("", "主帐户不存在!");

		// 有效余额为总余额 - 已冻结金额 - 待审核充值金额 - 待审核赎回资金
		// BigInteger validBalance = mas.getBalance().subtract(
		// mas.getFreezeAmount()).subtract(mas.getAuthDeposit()).subtract(
		// mas.getAuthRedemp());
		BigInteger validBalance = mas.getBalance().subtract(
				mas.getFreezeAmount()).subtract(mas.getAuthRedemp());
		validBalance = validBalance.add(creditAmt).subtract(debitAmt).subtract(
				mmObj.fee);
		if (validBalance.compareTo(BigInteger.ZERO) < 0)
			throw new CardTranApplicationException(TuxedoRespCode.RC_LACK, "卡"
					+ mmObj.cardNo + "记帐后有效余额不足,请核实!");

		// 取所有的子帐户
		List<SubAccount> subacclist = subAccountDaoImpl
				.getAllSubAccountForPaidByMasterAccNo(ac.getMasterAccNo());
		Map<BigInteger, BigInteger> submap = new HashMap<BigInteger, BigInteger>();
		Map<BigInteger, SubAccount> subaccmap = new HashMap<BigInteger, SubAccount>();
		for (SubAccount sa : subacclist) {
			submap.put(sa.getSubAccNo(), sa.getBalance());
			subaccmap.put(sa.getSubAccNo(), sa);
		}

		List<MchAccDetail> madlist = new ArrayList<MchAccDetail>();
		ArrayList<AccountParam> accountParamList = new ArrayList<AccountParam>();

		// 子帐户记帐金额分配
		Integer seqno = 0;
		ArrayList<AccountParam> apl = new ArrayList<AccountParam>();

		if (!BigInteger.ZERO.equals(debitAmt)) {
			distributeMistake(submap, subacclist, apl, debitAmt, true,
					AccountSystemAddTradDetail.TRANTYPE_CONSUME, "消费调整["
							+ mmObj.remark + "]");
			genMistakeAccDetail(apl, 0, seqno, madlist);
			seqno += apl.size();
			accountParamList.addAll(apl);
			apl.clear();
		}

		if (!BigInteger.ZERO.equals(creditAmt)) {
			distributeMistake(submap, subacclist, apl, creditAmt, false,
					AccountSystemAddTradDetail.TRANTYPE_DEPOSIT, "充值调整["
							+ mmObj.remark + "]");
			genMistakeAccDetail(apl, 1, seqno, madlist);
			seqno += apl.size();
			accountParamList.addAll(apl);
			apl.clear();
		}

		if (!BigInteger.ZERO.equals(mmObj.fee)) {
			distributeMistake(submap, subacclist, apl, mmObj.fee, true,
					AccountSystemAddTradDetail.TRANTYPE_FEE, "手续费调整["
							+ mmObj.remark + "]");
			genMistakeAccDetail(apl, 2, seqno, madlist);
			seqno += apl.size();
			accountParamList.addAll(apl);
			apl.clear();
		}

		Date tranTime = pcpsCommonDaoImpl.getDatabaseSysTime();
		mmObj.tranSeqNo = generateMerchantTranSeqNo(tranTime);

		// 执行帐户子系统记帐
		String accSeqNo = accountBusinessImpl.Account(issueCompany
				.getIssueCompanyNo(), mmObj.tranDate, tranTime,
				mmObj.tranSeqNo, mmObj.operId, "差错调整[" + mmObj.remark + "]",
				accountParamList);

		// 重新取卡信息
		ac = activateCardDaoImpl.getActivateCardByCardNo(mmObj.cardNo);
		mmObj.aftBalance = ac.getBalance();

		// 记载主明细表
		MchTradDetail mtd = new MchTradDetail();
		mtd.setMerchantNo(mmObj.merchantNo);
		mtd.setTranDate(mmObj.tranDate);
		mtd.setTranSeqNo(mmObj.tranSeqNo);
		mtd.setTranTime(tranTime);
		mtd.setMrchSeqNo(mmObj.tranSeqNo + "_MISTAKE");
		mtd.setCardNo(mmObj.cardNo);
		mtd.setAccAckSeqNo(accSeqNo);
		mtd.setTranCode(tranCode);
		mtd.setAmount(mmObj.amount);
		mtd.setScorePaidAmount(BigInteger.ZERO);
		mtd.setFactScorePaidAmount(BigInteger.ZERO);
		mtd.setFee(mmObj.fee);

		// 商户业务摘要
		mtd.setMrchAbstract(mmObj.remark);
		// 其他辅助信息 -- 终端 &机构
		mtd.setTerminalNo(mmObj.terminalNo);
		mtd.setMrchBrchNo(mmObj.merchantBranchNo);
		mtd.setAdjustOperId(mmObj.operId);

		// 插入主明细表
		mchTradDetailDaoImpl.insert(mtd);

		// 记载附加产品信息
		// createMchAddTradDetail(mtp);

		// 记载支付附加信息
		// createMchAddTradSummary(mtp);

		// 创建附加卡详细信息
		MchAddCardDetail cd = new MchAddCardDetail();

		cd.setMerchantNo(mtd.getMerchantNo());
		cd.setTranDate(mtd.getTranDate());
		cd.setTranSeqNo(mtd.getTranSeqNo());

		cd.setCardNo(mmObj.cardNo);
		cd.setAmount(mmObj.amount);

		mchAddCardDetailDaoImpl.insert(cd);

		// 创建附加记帐信息
		for (MchAccDetail mad : madlist) {
			SubAccount sa = subaccmap.get(mad.getSubAccNo());
			mad.setAccSeqNo(accSeqNo);
			mad.setMerchantNo(mtd.getMerchantNo());
			mad.setTranDate(mtd.getTranDate());
			mad.setTranSeqNo(mtd.getTranSeqNo());
			mad.setCardNo(mmObj.cardNo);
			mad.setMasterAccNo(ac.getMasterAccNo());
			mad.setOpenBranchNo(sa.getOpenBranchNo());
			mad.setSalesGroupId(sa.getSalesGroupId());
			mchAccDetailDaoImpl.insert(mad);
		}

		// 创建业务类型附加信息
		if (!PCPSUtil.isEmpty(mmObj.businessType)
				&& !BigInteger.ZERO.equals(debitAmt)) {
			MchBsTypeTradDetail td = new MchBsTypeTradDetail();

			td.setMerchantNo(mtd.getMerchantNo());
			td.setTranDate(mtd.getTranDate());
			td.setTranSeqNo(mtd.getTranSeqNo());

			td.setBusinessType(mmObj.businessType);
			td.setCardAmount(debitAmt);
			td.setScoreAmount(BigInteger.ZERO);
			td.setOtherAmount(BigInteger.ZERO);

			mchBsTypeTradDetailDaoImpl.insert(td);
		}

		return mmObj;
	}

	public void VerifyPaidCancel(MrchTransParam mtp, String cardNo, String pwd)
			throws PCPSApplicationException {

		// 如果未设置卡号
		if (PCPSUtil.isEmpty(cardNo))
			return;

		// 检查卡号与流水号的对应关系
		// if (PCPSUtil.isEmpty(pwd))
		// throw new TranApplicationException(TuxedoRespCode.RC_PWDERR,
		// "撤销/退货交易未设置卡密码!");

		// 检查卡号是否位于原交易之中
		List<MchAddCardDetail> macdlist = mchAddCardDetailDaoImpl
				.getAllMchAddCardDetailByTranSeqNo(mtp.merchantNo,
						mtp.tranDate, mtp.cancelTranSeqNo);
		if (macdlist == null || macdlist.size() < 1)
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID_TRANS,
					"撤销/退货交易未找到!");

		boolean isfound = false;

		for (MchAddCardDetail macd : macdlist) {
			if (PCPSUtil.compareObject(cardNo, macd.getCardNo())) {
				isfound = true;
				break;
			}
		}

		if (!isfound)
			throw new TranApplicationException(TuxedoRespCode.RC_INVALID,
					"撤销/退货交易非本卡产生的交易!");

		// 检查密码
		// cardTransBusinessImpl.verifyCardPassword(cardNo, pwd);
	}
}